daiso 1.2.7 → 1.2.9
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 +17 -17
- package/dist/api/actionsProxy.js +0 -10
- package/dist/api/cgvHandlers.js +4 -3
- package/dist/api/configStatus.js +7 -2
- package/dist/api/gs25Handlers.js +4 -3
- package/dist/api/handlers.js +11 -7
- package/dist/api/healthCheckDefinitions.js +0 -9
- package/dist/api/healthChecks.js +0 -1
- package/dist/api/lottecinemaHandlers.js +4 -3
- package/dist/api/lottemartHandlers.js +5 -142
- package/dist/api/megaboxHandlers.js +4 -3
- package/dist/api/opinetHandlers.js +2 -1
- package/dist/api/routes/cgvRoutes.js +3 -3
- package/dist/api/routes/cuRoutes.js +2 -2
- package/dist/api/routes/gs25Routes.js +3 -3
- package/dist/api/routes/lottecinemaRoutes.js +3 -3
- package/dist/api/routes/lottemartRoutes.js +4 -16
- package/dist/api/routes/megaboxRoutes.js +3 -3
- package/dist/api/routes/opinetRoutes.js +4 -4
- package/dist/cli.js +5 -5
- package/dist/cliHelp.js +0 -3
- package/dist/cliHelpDefinitions.js +2 -19
- package/dist/index.js +10 -11
- package/dist/middleware/dailyRateLimit.js +0 -1
- package/dist/pages/openapiFullSpec.js +0 -2
- package/dist/pages/openapiSpecActionParameters.js +4 -4
- package/dist/pages/openapiSpecComponents.js +0 -2
- package/dist/pages/openapiSpecPathsMegabox.js +1 -1
- package/dist/pages/promptReferenceText.js +0 -5
- package/dist/pages/promptRetailCinemaText.js +0 -26
- package/dist/services/cgv/index.js +3 -6
- package/dist/services/cgv/location.js +38 -125
- package/dist/services/cgv/tools/findTheaters.js +5 -4
- package/dist/services/cgv/tools/getTimetable.js +5 -4
- package/dist/services/cgv/tools/searchMovies.js +5 -4
- package/dist/services/cu/client.js +3 -35
- package/dist/services/cu/index.js +1 -0
- package/dist/services/cu/tools/checkInventory.js +3 -1
- package/dist/services/gs25/client.js +3 -33
- package/dist/services/gs25/index.js +2 -2
- package/dist/services/gs25/tools/checkInventory.js +3 -1
- package/dist/services/gs25/tools/findNearbyStores.js +3 -1
- package/dist/services/lottecinema/index.js +3 -6
- package/dist/services/lottecinema/location.js +13 -100
- package/dist/services/lottecinema/tools/findNearbyTheaters.js +7 -6
- package/dist/services/lottecinema/tools/getRemainingSeats.js +5 -4
- package/dist/services/lottecinema/tools/listNowShowing.js +5 -4
- package/dist/services/lottemart/client.js +5 -32
- package/dist/services/lottemart/geocode.js +3 -46
- package/dist/services/megabox/index.js +9 -8
- package/dist/services/megabox/location.js +19 -84
- package/dist/services/megabox/tools/findNearbyTheaters.js +7 -7
- package/dist/services/megabox/tools/getRemainingSeats.js +5 -5
- package/dist/services/megabox/tools/listNowShowing.js +5 -5
- package/dist/services/oliveyoung/tools/checkInventory.js +5 -5
- package/dist/services/oliveyoung/tools/findNearbyStores.js +3 -3
- package/dist/services/oliveyoung/tools/searchProducts.js +3 -3
- package/dist/services/oliveyoung/transport.js +49 -33
- package/dist/services/opinet/client.js +1 -1
- package/dist/services/opinet/index.js +1 -1
- package/dist/services/opinet/location.js +5 -56
- package/dist/services/opinet/tools.js +2 -2
- package/dist/utils/geocode.js +155 -0
- package/dist/utils/http.js +2 -2
- package/dist/utils/relayQuota.js +4 -2
- package/openapi.json +4 -183
- package/openapi.yaml +4 -131
- package/package.json +1 -2
- package/dist/pages/openapiSpecComponentsLotteMart.js +0 -85
- package/dist/pages/openapiSpecPathsLotteMart.js +0 -157
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
# Daiso MCP 및 Skill
|
|
11
11
|
|
|
12
|
-
다이소(제품/매장/재고), 상품 가격 비교, 주변 음식점/카페, 주유소/유가, 개발자 요청 제출,
|
|
12
|
+
다이소(제품/매장/재고), 상품 가격 비교, 주변 음식점/카페, 주유소/유가, 개발자 요청 제출, GS25(매장/상품/재고), 세븐일레븐(상품/매장/재고/인기검색어/카탈로그), CU(매장/재고), 이마트24(매장/상품/재고), 올리브영(매장/재고), 메가박스(지점/영화/시간표/좌석), 롯데시네마(지점/영화/좌석), CGV(극장/영화/시간표), 디트릭스(독립·예술영화관/상영작/잔여 좌석) 조회 기능을 MCP, CLI, Codex Skill로 AI에 연결합니다.
|
|
13
13
|
|
|
14
14
|
디트릭스의 지원 범위와 CLI 예시는 [독립·예술영화관 조회 가이드](docs/dtryx.md)를 참고하세요.
|
|
15
15
|
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
</tr>
|
|
58
58
|
<tr>
|
|
59
59
|
<td>리테일</td>
|
|
60
|
-
<td>다이소,
|
|
60
|
+
<td>다이소, 올리브영</td>
|
|
61
61
|
<td>상품, 매장, 재고</td>
|
|
62
62
|
</tr>
|
|
63
63
|
<tr>
|
|
@@ -85,17 +85,17 @@
|
|
|
85
85
|
**[실시간 서비스 상태 보기](https://aka-page.betteruptime.com/)**
|
|
86
86
|
|
|
87
87
|
<!-- WORKERS_INVOCATIONS_CHART:START -->
|
|
88
|
-
<h3>Cloudflare 요청 수 (2026-
|
|
88
|
+
<h3>Cloudflare 요청 수 (2026-09-05 ~ 2026-10-04, 30일)</h3>
|
|
89
89
|
|
|
90
|
-
<img src="./assets/analytics/workers-invocations.png?v=2026-
|
|
90
|
+
<img src="./assets/analytics/workers-invocations.png?v=2026-10-04T18:39:37.051Z" alt="Cloudflare 요청 수 그래프 (2026-09-05 ~ 2026-10-04)" width="100%">
|
|
91
91
|
|
|
92
|
-
<sub>기준 워커: <code>daiso-mcp</code> · 마지막 갱신: 2026-
|
|
92
|
+
<sub>기준 워커: <code>daiso-mcp</code> · 마지막 갱신: 2026-10-05 03:39 KST</sub>
|
|
93
93
|
<br><sub>집계: Worker 실행 + 루트 GET 리디렉션 요청 · 사용자 수와 다릅니다.</sub>
|
|
94
94
|
|
|
95
95
|
</div>
|
|
96
96
|
|
|
97
97
|
> [!IMPORTANT]
|
|
98
|
-
> 최근 공개 서버 사용량이 크게 증가하여 2026년 7월 18일부터 올리브영·CGV·CU·GS25
|
|
98
|
+
> 최근 공개 서버 사용량이 크게 증가하여 2026년 7월 18일부터 올리브영·CGV·CU·GS25의 검색을 포함한 공개 GET API에 IP당 하루 합산 3,000회(KST 기준)의 호출 제한을 적용합니다. 한도를 초과하는 사용이 필요하다면 Daiso MCP는 오픈 소스이므로 이 저장소를 직접 배포해 이용해 주세요.
|
|
99
99
|
|
|
100
100
|
<div align="center">
|
|
101
101
|
|
|
@@ -136,8 +136,6 @@ ChatGPT, Claude, Home Assistant, Grok 같은 AI 앱에서 바로 연결해 사
|
|
|
136
136
|
콜라 어디가 싼지 비교해줘
|
|
137
137
|
올리브영 mcp로 명동 근처 매장 찾아줘
|
|
138
138
|
이마트24 mcp로 강남 근처 매장과 두바이 재고 알려줘
|
|
139
|
-
롯데마트 mcp로 잠실 근처 매장 찾아줘
|
|
140
|
-
롯데마트 mcp로 강변점에서 콜라 재고 알려줘
|
|
141
139
|
GS25 mcp로 강남 근처 매장과 오감자 재고 알려줘
|
|
142
140
|
세븐일레븐 mcp로 삼각김밥 검색해줘
|
|
143
141
|
세븐일레븐 mcp로 안산 중앙역 근처 매장 찾아줘
|
|
@@ -183,8 +181,6 @@ CGV mcp로 강남 상영 영화와 시간표 알려줘
|
|
|
183
181
|
올리브영 mcp를 사용해서 명동 근처 매장 찾아줘
|
|
184
182
|
올리브영 mcp를 사용해서 선크림 재고 확인해줘
|
|
185
183
|
이마트24 mcp를 사용해서 강남 매장 찾고 두바이 재고 확인해줘
|
|
186
|
-
롯데마트 mcp를 사용해서 잠실 근처 매장 찾아줘
|
|
187
|
-
롯데마트 mcp를 사용해서 강변점에서 콜라 검색해줘
|
|
188
184
|
GS25 mcp를 사용해서 강남 매장 찾고 오감자 재고 확인해줘
|
|
189
185
|
세븐일레븐 mcp를 사용해서 안산 중앙역 근처 매장 찾고 핫식스 재고 확인해줘
|
|
190
186
|
메가박스 mcp를 사용해서 강남역 근처 지점 찾아줘
|
|
@@ -318,8 +314,6 @@ npx daiso cu-inventory 과자 --storeKeyword 강남
|
|
|
318
314
|
npx daiso cgv-theaters 강남 --limit 10
|
|
319
315
|
npx daiso cgv-movies --playDate 20260307 --theaterCode 0056
|
|
320
316
|
npx daiso cgv-timetable --playDate 20260307 --theaterCode 0056
|
|
321
|
-
npx daiso lottemart-stores 잠실 --area 서울 --limit 10
|
|
322
|
-
npx daiso lottemart-products 콜라 --storeName 강변점 --area 서울
|
|
323
317
|
npx daiso emart24-stores 강남 --service24h true
|
|
324
318
|
npx daiso emart24-products 두바이 --pageSize 20
|
|
325
319
|
npx daiso emart24-inventory 8800244010504 --bizNoArr 28339,05015
|
|
@@ -404,7 +398,7 @@ GET /api/opinet/stations/around?x=314681.8&y=544837&radiusMeters=3000&fuelCode=B
|
|
|
404
398
|
GET /api/opinet/station?id=A0010207
|
|
405
399
|
```
|
|
406
400
|
|
|
407
|
-
오피넷 API 키는 `OPINET_API_KEY` 환경 변수 또는 Cloudflare Worker Secret으로 설정합니다. 키 발급은 오피넷 웹사이트의 `유가관련정보 > 유가정보 API > 인증키 발급`에서 진행합니다. 반경 검색은 `lat/lng`, `location`, KATEC `x/y`를 모두 지원하며, `location` 검색에는 `
|
|
401
|
+
오피넷 API 키는 `OPINET_API_KEY` 환경 변수 또는 Cloudflare Worker Secret으로 설정합니다. 키 발급은 오피넷 웹사이트의 `유가관련정보 > 유가정보 API > 인증키 발급`에서 진행합니다. 반경 검색은 `lat/lng`, `location`, KATEC `x/y`를 모두 지원하며, `location` 검색에는 `KAKAO_REST_API_KEY`를 설정합니다. 역명·상호명은 네이버 지역 검색으로 보완합니다. 자세한 설정은 [무료 위치 검색 가이드](./docs/free-location-search.md)를 확인하세요.
|
|
408
402
|
|
|
409
403
|
운영 제약 및 캐싱 정책:
|
|
410
404
|
|
|
@@ -415,7 +409,7 @@ GET /api/opinet/station?id=A0010207
|
|
|
415
409
|
- `GET /api/opinet/lowest`: 30분 캐시, 10분 stale-while-revalidate
|
|
416
410
|
- `GET /api/opinet/stations/around`: 20분 캐시, 5분 stale-while-revalidate
|
|
417
411
|
- `GET /api/opinet/station`: 60분 캐시, 10분 stale-while-revalidate
|
|
418
|
-
-
|
|
412
|
+
- 주소·장소명 변환은 카카오 주소/키워드 검색을 사용합니다. 직접 좌표를 입력하면 위치 변환 API를 호출하지 않습니다.
|
|
419
413
|
- 호출 한도에 자주 도달하면 운영자가 담당 기관인 한국석유공사/오피넷에 문의해 추가 할당량 또는 별도 이용 조건을 협의할 예정입니다.
|
|
420
414
|
|
|
421
415
|
### 개발자 요청 제출
|
|
@@ -437,6 +431,10 @@ GET /api/feedback/requests?type=bug&title=제목&description=설명
|
|
|
437
431
|
|
|
438
432
|
로컬 실행이나 배포 환경에는 `SUPABASE_URL`, `SUPABASE_SERVICE_ROLE_KEY`를 설정하세요. 테이블 스키마는 [supabase-agent-requests.sql](./docs/supabase-agent-requests.sql)에 있습니다.
|
|
439
433
|
|
|
434
|
+
### 무료 위치 검색
|
|
435
|
+
|
|
436
|
+
주소·역명·상호명을 좌표로 바꾸는 기능은 카카오맵 무료 API를 사용합니다. Google Geocoding과 Zyte는 호출하지 않습니다. 카카오 무료 쿼터 대상 확인, 환경 변수와 실패 시 동작은 [무료 위치 검색 가이드](./docs/free-location-search.md)에 있습니다.
|
|
437
|
+
|
|
440
438
|
### 운영 헬스 체크
|
|
441
439
|
|
|
442
440
|
공개 상태 페이지: **[Daiso MCP Status](https://aka-page.betteruptime.com/)**
|
|
@@ -453,7 +451,7 @@ curl -H "Authorization: Bearer $HEALTH_CHECK_SECRET" \
|
|
|
453
451
|
주요 쿼리:
|
|
454
452
|
|
|
455
453
|
- `service=gs25`: 특정 서비스만 확인
|
|
456
|
-
- `check=
|
|
454
|
+
- `check=daiso.products`: 특정 체크만 확인
|
|
457
455
|
- `mode=quick|deep|full`: 체크 모드 선택
|
|
458
456
|
- `fresh=true`: 60초 캐시 우회
|
|
459
457
|
- `includeSamples=true`: 첫 결과 이름 샘플 포함
|
|
@@ -484,7 +482,7 @@ curl -H "x-health-check-key: $HEALTH_CHECK_SECRET" \
|
|
|
484
482
|
"https://mcp.aka.page/api/rate-limit/stats?service=cgv"
|
|
485
483
|
```
|
|
486
484
|
|
|
487
|
-
쿼리 필터는 `from`, `to`, `service`입니다. `from`과 `to`는 함께 지정하거나 둘 다 생략해야 하며 날짜 형식은 `YYYY-MM-DD`입니다. `service`에는 `oliveyoung`, `cgv`, `cu`, `gs25`, `lottemart`만 사용할 수 있습니다. 날짜를 생략하면 현재 KST 일자를 포함한 최근 7일을 조회합니다. 조회 가능한 보관 범위는 현재 KST 일자와 그 이전 29일이며, 한 번에 KST 달력 날짜 기준 최대 30일을 요청할 수 있습니다.
|
|
485
|
+
쿼리 필터는 `from`, `to`, `service`입니다. `from`과 `to`는 함께 지정하거나 둘 다 생략해야 하며 날짜 형식은 `YYYY-MM-DD`입니다. `service`에는 `oliveyoung`, `cgv`, `cu`, `gs25`, `lottemart`만 사용할 수 있습니다. `lottemart` 필터는 지원 중단 전 호출 제한 통계를 조회하기 위해 유지합니다. 날짜를 생략하면 현재 KST 일자를 포함한 최근 7일을 조회합니다. 조회 가능한 보관 범위는 현재 KST 일자와 그 이전 29일이며, 한 번에 KST 달력 날짜 기준 최대 30일을 요청할 수 있습니다.
|
|
488
486
|
|
|
489
487
|
성공 응답은 전체 합계와 일별·서비스별 차단 요청 수와 고유 차단 주체 수를 제공합니다.
|
|
490
488
|
|
|
@@ -640,7 +638,9 @@ daiso 인터랙티브 모드
|
|
|
640
638
|
- [이마트24 앱 스크래핑 준비 가이드](./docs/emart24-app-scraping-preparation-guide.md)
|
|
641
639
|
- [이마트24 앱 스크래핑 리플레이 가이드](./docs/emart24-app-scraping-replay-guide.md)
|
|
642
640
|
|
|
643
|
-
### 롯데마트
|
|
641
|
+
### 롯데마트 과거 분석
|
|
642
|
+
|
|
643
|
+
롯데마트 지원은 중단했습니다. 기존 REST 경로는 `410 SERVICE_RETIRED`, 기존 CLI 명령은 지원 중단 오류를 반환합니다.
|
|
644
644
|
|
|
645
645
|
- [롯데마트 모바일 도와센터 스크래핑 리플레이 계획](./docs/lottemart-mobile-scraping-replay-plan.md)
|
|
646
646
|
|
package/dist/api/actionsProxy.js
CHANGED
|
@@ -51,16 +51,6 @@ export const ACTION_QUERY_DEFINITIONS = [
|
|
|
51
51
|
summary: '이마트24 재고 조회',
|
|
52
52
|
targetPath: '/api/emart24/inventory',
|
|
53
53
|
},
|
|
54
|
-
{
|
|
55
|
-
action: 'lottemartFindStores',
|
|
56
|
-
summary: '롯데마트 매장 검색',
|
|
57
|
-
targetPath: '/api/lottemart/stores',
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
action: 'lottemartSearchProducts',
|
|
61
|
-
summary: '롯데마트 상품 검색',
|
|
62
|
-
targetPath: '/api/lottemart/products',
|
|
63
|
-
},
|
|
64
54
|
{ action: 'gs25FindStores', summary: 'GS25 매장 검색', targetPath: '/api/gs25/stores' },
|
|
65
55
|
{ action: 'gs25SearchProducts', summary: 'GS25 상품 검색', targetPath: '/api/gs25/products' },
|
|
66
56
|
{ action: 'gs25CheckInventory', summary: 'GS25 재고 조회', targetPath: '/api/gs25/inventory' },
|
package/dist/api/cgvHandlers.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { geocodeBindings } from '../utils/geocode.js';
|
|
1
2
|
/**
|
|
2
3
|
* CGV GET API 핸들러
|
|
3
4
|
*/
|
|
@@ -38,7 +39,7 @@ export async function handleCgvFindTheaters(c) {
|
|
|
38
39
|
}, {
|
|
39
40
|
timeout: timeoutMs,
|
|
40
41
|
zyteApiKey: c.env?.ZYTE_API_KEY,
|
|
41
|
-
|
|
42
|
+
...geocodeBindings(c.env),
|
|
42
43
|
});
|
|
43
44
|
return successResponse(c, result, { total: result.count, pageSize: limit });
|
|
44
45
|
}
|
|
@@ -92,7 +93,7 @@ export async function handleCgvSearchMovies(c) {
|
|
|
92
93
|
}, {
|
|
93
94
|
timeout: timeoutMs,
|
|
94
95
|
zyteApiKey: c.env?.ZYTE_API_KEY,
|
|
95
|
-
|
|
96
|
+
...geocodeBindings(c.env),
|
|
96
97
|
});
|
|
97
98
|
resolvedTheater = resolved.theater;
|
|
98
99
|
theaterCode = resolved.theater?.theaterCode;
|
|
@@ -152,7 +153,7 @@ export async function handleCgvGetTimetable(c) {
|
|
|
152
153
|
}, {
|
|
153
154
|
timeout: timeoutMs,
|
|
154
155
|
zyteApiKey: c.env?.ZYTE_API_KEY,
|
|
155
|
-
|
|
156
|
+
...geocodeBindings(c.env),
|
|
156
157
|
});
|
|
157
158
|
resolvedTheater = resolved.theater;
|
|
158
159
|
theaterCode = resolved.theater?.theaterCode;
|
package/dist/api/configStatus.js
CHANGED
|
@@ -49,7 +49,12 @@ export function buildConfigStatus(bindings) {
|
|
|
49
49
|
},
|
|
50
50
|
googleMapsApiKey: {
|
|
51
51
|
configured: isConfigured(bindings?.GOOGLE_MAPS_API_KEY),
|
|
52
|
-
|
|
52
|
+
enabled: false,
|
|
53
|
+
usedBy: [],
|
|
54
|
+
},
|
|
55
|
+
kakaoRestApiKey: {
|
|
56
|
+
configured: isConfigured(bindings?.KAKAO_REST_API_KEY),
|
|
57
|
+
usedBy: ['gs25', 'cu', 'opinet', 'megabox', 'lottecinema', 'cgv'],
|
|
53
58
|
},
|
|
54
59
|
zyteApiKey: {
|
|
55
60
|
configured: isConfigured(bindings?.ZYTE_API_KEY),
|
|
@@ -58,7 +63,7 @@ export function buildConfigStatus(bindings) {
|
|
|
58
63
|
},
|
|
59
64
|
naverLocalSearch: {
|
|
60
65
|
configured: isConfigured(bindings?.NAVER_CLIENT_ID) && isConfigured(bindings?.NAVER_CLIENT_SECRET),
|
|
61
|
-
usedBy: ['places'],
|
|
66
|
+
usedBy: ['places', 'gs25', 'cu', 'opinet', 'megabox', 'lottecinema', 'cgv'],
|
|
62
67
|
},
|
|
63
68
|
opinetApiKey: {
|
|
64
69
|
configured: isConfigured(bindings?.OPINET_API_KEY),
|
package/dist/api/gs25Handlers.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { geocodeBindings } from '../utils/geocode.js';
|
|
1
2
|
/**
|
|
2
3
|
* GS25 GET API 핸들러
|
|
3
4
|
*/
|
|
@@ -26,7 +27,7 @@ export async function handleGs25FindStores(c) {
|
|
|
26
27
|
keyword.trim().length > 0) {
|
|
27
28
|
const geocoded = await geocodeGs25Address(keyword, {
|
|
28
29
|
timeout: 15000,
|
|
29
|
-
|
|
30
|
+
...geocodeBindings(c.env),
|
|
30
31
|
});
|
|
31
32
|
if (geocoded) {
|
|
32
33
|
latitude = geocoded.latitude;
|
|
@@ -168,7 +169,7 @@ export async function handleGs25CheckInventory(c) {
|
|
|
168
169
|
storeKeyword.trim().length > 0) {
|
|
169
170
|
const directGeocoded = await geocodeGs25Address(storeKeyword, {
|
|
170
171
|
timeout: 15000,
|
|
171
|
-
|
|
172
|
+
...geocodeBindings(c.env),
|
|
172
173
|
});
|
|
173
174
|
if (directGeocoded) {
|
|
174
175
|
latitude = directGeocoded.latitude;
|
|
@@ -187,7 +188,7 @@ export async function handleGs25CheckInventory(c) {
|
|
|
187
188
|
if (firstAddress.length > 0) {
|
|
188
189
|
const geocoded = await geocodeGs25Address(firstAddress, {
|
|
189
190
|
timeout: 15000,
|
|
190
|
-
|
|
191
|
+
...geocodeBindings(c.env),
|
|
191
192
|
});
|
|
192
193
|
if (geocoded) {
|
|
193
194
|
latitude = geocoded.latitude;
|
package/dist/api/handlers.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { geocodeBindings } from '../utils/geocode.js';
|
|
1
2
|
/**
|
|
2
3
|
* GET API 핸들러
|
|
3
4
|
*
|
|
@@ -32,8 +33,9 @@ export async function handleOliveyoungSearchProducts(c) {
|
|
|
32
33
|
const size = parseInt(c.req.query('size') || '20');
|
|
33
34
|
const sort = c.req.query('sort') || '01';
|
|
34
35
|
const includeSoldOut = c.req.query('includeSoldOut') === 'true';
|
|
35
|
-
const
|
|
36
|
-
const
|
|
36
|
+
const defaultTimeoutMs = c.env?.OY_RELAY_URL ? 60000 : 15000;
|
|
37
|
+
const parsedTimeoutMs = parseInt(c.req.query('timeoutMs') || '', 10);
|
|
38
|
+
const timeoutMs = Number.isFinite(parsedTimeoutMs) && parsedTimeoutMs > 0 ? parsedTimeoutMs : defaultTimeoutMs;
|
|
37
39
|
if (keyword.trim().length === 0) {
|
|
38
40
|
return errorResponse(c, 'MISSING_QUERY', '검색어(keyword)를 입력해주세요.');
|
|
39
41
|
}
|
|
@@ -76,8 +78,9 @@ export async function handleOliveyoungFindStores(c) {
|
|
|
76
78
|
const lng = parseFloat(c.req.query('lng') || '126.978');
|
|
77
79
|
const pageIdx = parseInt(c.req.query('pageIdx') || '1');
|
|
78
80
|
const limit = parseInt(c.req.query('limit') || '20');
|
|
79
|
-
const
|
|
80
|
-
const
|
|
81
|
+
const defaultTimeoutMs = c.env?.OY_RELAY_URL ? 60000 : 15000;
|
|
82
|
+
const parsedTimeoutMs = parseInt(c.req.query('timeoutMs') || '', 10);
|
|
83
|
+
const timeoutMs = Number.isFinite(parsedTimeoutMs) && parsedTimeoutMs > 0 ? parsedTimeoutMs : defaultTimeoutMs;
|
|
81
84
|
try {
|
|
82
85
|
const result = await fetchOliveyoungStores({
|
|
83
86
|
latitude: lat,
|
|
@@ -116,8 +119,9 @@ export async function handleOliveyoungCheckInventory(c) {
|
|
|
116
119
|
const sort = c.req.query('sort') || '01';
|
|
117
120
|
const includeSoldOut = c.req.query('includeSoldOut') === 'true';
|
|
118
121
|
const storeLimit = parseInt(c.req.query('storeLimit') || '10');
|
|
119
|
-
const
|
|
120
|
-
const
|
|
122
|
+
const defaultTimeoutMs = c.env?.OY_RELAY_URL ? 60000 : 15000;
|
|
123
|
+
const parsedTimeoutMs = parseInt(c.req.query('timeoutMs') || '', 10);
|
|
124
|
+
const timeoutMs = Number.isFinite(parsedTimeoutMs) && parsedTimeoutMs > 0 ? parsedTimeoutMs : defaultTimeoutMs;
|
|
121
125
|
const parsedStockCheckLimit = parseInt(c.req.query('stockCheckLimit') || '5', 10);
|
|
122
126
|
const stockCheckLimit = Number.isFinite(parsedStockCheckLimit) && parsedStockCheckLimit >= 0 ? parsedStockCheckLimit : 5;
|
|
123
127
|
if (!keyword || keyword.trim().length === 0) {
|
|
@@ -272,7 +276,7 @@ export async function handleCuCheckInventory(c) {
|
|
|
272
276
|
if (firstAddress.length > 0) {
|
|
273
277
|
const geocoded = await geocodeCuAddress(firstAddress, {
|
|
274
278
|
timeout: 15000,
|
|
275
|
-
|
|
279
|
+
...geocodeBindings(c.env),
|
|
276
280
|
});
|
|
277
281
|
if (geocoded) {
|
|
278
282
|
const hasStockSeed = !!firstStockItem?.itemCode;
|
|
@@ -117,15 +117,6 @@ export const HEALTH_CHECKS = [
|
|
|
117
117
|
degradedFailurePatterns: SEVENELEVEN_UPSTREAM_403_PATTERNS,
|
|
118
118
|
allowEmpty: true,
|
|
119
119
|
},
|
|
120
|
-
{
|
|
121
|
-
id: 'lottemart.products',
|
|
122
|
-
service: 'lottemart',
|
|
123
|
-
target: 'products',
|
|
124
|
-
mode: 'quick',
|
|
125
|
-
path: '/api/lottemart/products?keyword=%EC%BD%9C%EB%9D%BC&storeCode=2301&area=%EC%84%9C%EC%9A%B8&pageLimit=1&source=zetta',
|
|
126
|
-
collectionKey: 'products',
|
|
127
|
-
requiredFields: ['productCode', 'name', 'productName'],
|
|
128
|
-
},
|
|
129
120
|
{
|
|
130
121
|
id: 'oliveyoung.products',
|
|
131
122
|
service: 'oliveyoung',
|
package/dist/api/healthChecks.js
CHANGED
|
@@ -106,7 +106,6 @@ const CLI_CONTRACT_PATHS = [
|
|
|
106
106
|
'/api/gs25/stores?keyword=%EA%B0%95%EB%82%A8&limit=1',
|
|
107
107
|
'/api/seveneleven/products?query=%EC%BB%A4%ED%94%BC&size=1',
|
|
108
108
|
'/api/emart24/products?keyword=%EC%BB%A4%ED%94%BC&pageSize=1',
|
|
109
|
-
'/api/lottemart/products?keyword=%EC%BD%9C%EB%9D%BC&storeCode=2301&area=%EC%84%9C%EC%9A%B8&pageLimit=1',
|
|
110
109
|
'/api/oliveyoung/products?keyword=%EC%84%A0%ED%81%AC%EB%A6%BC&size=1',
|
|
111
110
|
'/api/megabox/theaters?keyword=%EA%B0%95%EB%82%A8&limit=1',
|
|
112
111
|
'/api/lottecinema/theaters?keyword=%EC%9E%A0%EC%8B%A4&limit=1',
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { geocodeBindings } from '../utils/geocode.js';
|
|
1
2
|
/**
|
|
2
3
|
* 롯데시네마 GET API 핸들러
|
|
3
4
|
*/
|
|
@@ -29,7 +30,7 @@ export async function handleLotteCinemaFindNearbyTheaters(c) {
|
|
|
29
30
|
timeout: timeoutMs,
|
|
30
31
|
}, {
|
|
31
32
|
timeout: timeoutMs,
|
|
32
|
-
|
|
33
|
+
...geocodeBindings(c.env),
|
|
33
34
|
});
|
|
34
35
|
return successResponse(c, result, { total: result.count, pageSize: limit });
|
|
35
36
|
}
|
|
@@ -57,7 +58,7 @@ export async function handleLotteCinemaListNowShowing(c) {
|
|
|
57
58
|
timeout: timeoutMs,
|
|
58
59
|
}, {
|
|
59
60
|
timeout: timeoutMs,
|
|
60
|
-
|
|
61
|
+
...geocodeBindings(c.env),
|
|
61
62
|
});
|
|
62
63
|
resolvedTheater = resolved.theater;
|
|
63
64
|
theaterId = resolved.theater?.theaterId;
|
|
@@ -111,7 +112,7 @@ export async function handleLotteCinemaGetRemainingSeats(c) {
|
|
|
111
112
|
timeout: timeoutMs,
|
|
112
113
|
}, {
|
|
113
114
|
timeout: timeoutMs,
|
|
114
|
-
|
|
115
|
+
...geocodeBindings(c.env),
|
|
115
116
|
});
|
|
116
117
|
resolvedTheater = resolved.theater;
|
|
117
118
|
theaterId = resolved.theater?.theaterId;
|
|
@@ -1,144 +1,7 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
*/
|
|
4
|
-
import { errorResponse, successResponse } from './response.js';
|
|
5
|
-
import { fetchLotteMartStores, searchLotteMartProducts } from '../services/lottemart/client.js';
|
|
6
|
-
import { DEFAULT_LOTTEMART_TIMEOUT_MS } from '../services/lottemart/config.js';
|
|
7
|
-
import { probeLotteMartUpstream } from '../services/lottemart/debug.js';
|
|
8
|
-
function parseLotteMartProductSource(value) {
|
|
9
|
-
if (!value || value === 'auto') {
|
|
10
|
-
return 'auto';
|
|
11
|
-
}
|
|
12
|
-
if (value === 'legacy' || value === 'zetta') {
|
|
13
|
-
return value;
|
|
14
|
-
}
|
|
15
|
-
return null;
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* 롯데마트 매장 검색 API 핸들러
|
|
19
|
-
* GET /api/lottemart/stores?keyword={키워드}&lat={위도}&lng={경도}
|
|
20
|
-
*/
|
|
1
|
+
/** 롯데마트 지원 종료 응답. 업스트림에는 요청하지 않습니다. */
|
|
2
|
+
import { errorResponse } from './response.js';
|
|
21
3
|
export async function handleLotteMartFindStores(c) {
|
|
22
|
-
|
|
23
|
-
const area = c.req.query('area') || '';
|
|
24
|
-
const brandVariant = c.req.query('brandVariant') || '';
|
|
25
|
-
const limit = parseInt(c.req.query('limit') || '20', 10);
|
|
26
|
-
const rawLat = c.req.query('lat');
|
|
27
|
-
const rawLng = c.req.query('lng');
|
|
28
|
-
const parsedLat = rawLat ? parseFloat(rawLat) : undefined;
|
|
29
|
-
const parsedLng = rawLng ? parseFloat(rawLng) : undefined;
|
|
30
|
-
const latitude = typeof parsedLat === 'number' && Number.isFinite(parsedLat) ? parsedLat : undefined;
|
|
31
|
-
const longitude = typeof parsedLng === 'number' && Number.isFinite(parsedLng) ? parsedLng : undefined;
|
|
32
|
-
try {
|
|
33
|
-
const result = await fetchLotteMartStores({
|
|
34
|
-
area,
|
|
35
|
-
keyword,
|
|
36
|
-
brandVariant,
|
|
37
|
-
latitude,
|
|
38
|
-
longitude,
|
|
39
|
-
limit,
|
|
40
|
-
}, {
|
|
41
|
-
timeout: DEFAULT_LOTTEMART_TIMEOUT_MS,
|
|
42
|
-
googleMapsApiKey: c.env?.GOOGLE_MAPS_API_KEY,
|
|
43
|
-
zyteApiKey: c.env?.ZYTE_API_KEY,
|
|
44
|
-
});
|
|
45
|
-
return successResponse(c, {
|
|
46
|
-
area: area || null,
|
|
47
|
-
keyword,
|
|
48
|
-
brandVariant: brandVariant || null,
|
|
49
|
-
geocodeUsed: result.geocodeUsed,
|
|
50
|
-
location: result.location,
|
|
51
|
-
count: result.stores.length,
|
|
52
|
-
stores: result.stores,
|
|
53
|
-
}, {
|
|
54
|
-
total: result.stores.length,
|
|
55
|
-
page: 1,
|
|
56
|
-
pageSize: limit,
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
catch (error) {
|
|
60
|
-
const message = error instanceof Error ? error.message : '알 수 없는 오류가 발생했습니다.';
|
|
61
|
-
return errorResponse(c, 'LOTTEMART_STORE_SEARCH_FAILED', message, 500);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* 롯데마트 상품 검색 API 핸들러
|
|
66
|
-
* GET /api/lottemart/products?keyword={검색어}&storeCode={매장코드}
|
|
67
|
-
*/
|
|
68
|
-
export async function handleLotteMartSearchProducts(c) {
|
|
69
|
-
const keyword = c.req.query('keyword') || '';
|
|
70
|
-
const area = c.req.query('area') || '';
|
|
71
|
-
const storeCode = c.req.query('storeCode') || '';
|
|
72
|
-
const storeName = c.req.query('storeName') || '';
|
|
73
|
-
const pageLimit = parseInt(c.req.query('pageLimit') || '3', 10);
|
|
74
|
-
const parsedTimeoutMs = parseInt(c.req.query('timeoutMs') || String(DEFAULT_LOTTEMART_TIMEOUT_MS), 10);
|
|
75
|
-
const timeoutMs = Number.isFinite(parsedTimeoutMs) && parsedTimeoutMs > 0 ? parsedTimeoutMs : DEFAULT_LOTTEMART_TIMEOUT_MS;
|
|
76
|
-
const source = parseLotteMartProductSource(c.req.query('source'));
|
|
77
|
-
if (keyword.trim().length === 0) {
|
|
78
|
-
return errorResponse(c, 'MISSING_QUERY', '검색어(keyword)를 입력해주세요.');
|
|
79
|
-
}
|
|
80
|
-
if (!source) {
|
|
81
|
-
return errorResponse(c, 'INVALID_LOTTEMART_SOURCE', 'source는 auto, legacy, zetta 중 하나여야 합니다.');
|
|
82
|
-
}
|
|
83
|
-
if (storeCode.trim().length === 0 && storeName.trim().length === 0) {
|
|
84
|
-
return errorResponse(c, 'MISSING_STORE', 'storeCode 또는 storeName 중 하나를 입력해주세요.');
|
|
85
|
-
}
|
|
86
|
-
try {
|
|
87
|
-
const result = await searchLotteMartProducts({
|
|
88
|
-
area,
|
|
89
|
-
storeCode,
|
|
90
|
-
storeName,
|
|
91
|
-
keyword,
|
|
92
|
-
pageLimit,
|
|
93
|
-
source,
|
|
94
|
-
}, {
|
|
95
|
-
timeout: timeoutMs,
|
|
96
|
-
zyteApiKey: c.env?.ZYTE_API_KEY,
|
|
97
|
-
});
|
|
98
|
-
return successResponse(c, {
|
|
99
|
-
area: result.area,
|
|
100
|
-
storeCode: result.storeCode,
|
|
101
|
-
storeName: result.storeName,
|
|
102
|
-
keyword,
|
|
103
|
-
pageLimit,
|
|
104
|
-
totalPages: result.totalPages,
|
|
105
|
-
products: result.products,
|
|
106
|
-
}, {
|
|
107
|
-
total: result.totalCount,
|
|
108
|
-
page: 1,
|
|
109
|
-
pageSize: result.products.length,
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
catch (error) {
|
|
113
|
-
const message = error instanceof Error ? error.message : '알 수 없는 오류가 발생했습니다.';
|
|
114
|
-
return errorResponse(c, 'LOTTEMART_PRODUCT_SEARCH_FAILED', message, 500);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
/**
|
|
118
|
-
* 롯데마트 업스트림 단독 진단 API 핸들러
|
|
119
|
-
* GET /api/lottemart/debug?target=market-options|stores|products|product-page
|
|
120
|
-
*/
|
|
121
|
-
export async function handleLotteMartDebug(c) {
|
|
122
|
-
const target = (c.req.query('target') || '');
|
|
123
|
-
const allowedTargets = ['market-options', 'stores', 'products', 'product-page'];
|
|
124
|
-
if (!allowedTargets.includes(target)) {
|
|
125
|
-
return errorResponse(c, 'INVALID_TARGET', 'target은 market-options, stores, products, product-page 중 하나여야 합니다.');
|
|
126
|
-
}
|
|
127
|
-
try {
|
|
128
|
-
const result = await probeLotteMartUpstream({
|
|
129
|
-
target,
|
|
130
|
-
area: c.req.query('area') || undefined,
|
|
131
|
-
type: (c.req.query('type') || undefined),
|
|
132
|
-
storeCode: c.req.query('storeCode') || undefined,
|
|
133
|
-
keyword: c.req.query('keyword') || undefined,
|
|
134
|
-
page: c.req.query('page') ? parseInt(c.req.query('page'), 10) : undefined,
|
|
135
|
-
timeout: c.req.query('timeoutMs') ? parseInt(c.req.query('timeoutMs'), 10) : DEFAULT_LOTTEMART_TIMEOUT_MS,
|
|
136
|
-
zyteApiKey: c.env?.ZYTE_API_KEY,
|
|
137
|
-
});
|
|
138
|
-
return successResponse(c, result);
|
|
139
|
-
}
|
|
140
|
-
catch (error) {
|
|
141
|
-
const message = error instanceof Error ? error.message : '알 수 없는 오류가 발생했습니다.';
|
|
142
|
-
return errorResponse(c, 'LOTTEMART_DEBUG_FAILED', message, 500);
|
|
143
|
-
}
|
|
4
|
+
return errorResponse(c, 'SERVICE_RETIRED', '롯데마트 서비스 지원이 종료되었습니다.', 410);
|
|
144
5
|
}
|
|
6
|
+
export const handleLotteMartSearchProducts = handleLotteMartFindStores;
|
|
7
|
+
export const handleLotteMartDebug = handleLotteMartFindStores;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { geocodeBindings } from '../utils/geocode.js';
|
|
1
2
|
/**
|
|
2
3
|
* 메가박스 GET API 핸들러
|
|
3
4
|
*/
|
|
@@ -33,7 +34,7 @@ export async function handleMegaboxFindNearbyTheaters(c) {
|
|
|
33
34
|
limit,
|
|
34
35
|
timeout: timeoutMs,
|
|
35
36
|
}, {
|
|
36
|
-
|
|
37
|
+
...geocodeBindings(c.env),
|
|
37
38
|
timeout: timeoutMs,
|
|
38
39
|
});
|
|
39
40
|
return successResponse(c, result, { total: result.count, pageSize: limit });
|
|
@@ -68,7 +69,7 @@ export async function handleMegaboxListNowShowing(c) {
|
|
|
68
69
|
playDate,
|
|
69
70
|
timeout: timeoutMs,
|
|
70
71
|
}, {
|
|
71
|
-
|
|
72
|
+
...geocodeBindings(c.env),
|
|
72
73
|
timeout: timeoutMs,
|
|
73
74
|
});
|
|
74
75
|
theaterId = resolved.theater?.theaterId;
|
|
@@ -130,7 +131,7 @@ export async function handleMegaboxGetRemainingSeats(c) {
|
|
|
130
131
|
playDate,
|
|
131
132
|
timeout: timeoutMs,
|
|
132
133
|
}, {
|
|
133
|
-
|
|
134
|
+
...geocodeBindings(c.env),
|
|
134
135
|
timeout: timeoutMs,
|
|
135
136
|
});
|
|
136
137
|
theaterId = resolved.theater?.theaterId;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { geocodeBindings } from '../utils/geocode.js';
|
|
1
2
|
import { getErrorMessage } from '../core/errors.js';
|
|
2
3
|
import { fetchOpinetAveragePrices, fetchOpinetLowestStations, fetchOpinetStationDetail, fetchOpinetStationsAround, } from '../services/opinet/client.js';
|
|
3
4
|
import { errorResponse, successResponse } from './response.js';
|
|
@@ -64,7 +65,7 @@ export async function handleOpinetStationsAround(c) {
|
|
|
64
65
|
sort: c.req.query('sort'),
|
|
65
66
|
}, {
|
|
66
67
|
apiKey: c.env?.OPINET_API_KEY,
|
|
67
|
-
|
|
68
|
+
...geocodeBindings(c.env),
|
|
68
69
|
timeoutMs: parseInteger(c.req.query('timeoutMs')),
|
|
69
70
|
});
|
|
70
71
|
return successResponse(c, result, { total: result.count, page: 1, pageSize: result.count });
|
|
@@ -7,16 +7,16 @@ export function registerCgvRoutes(app) {
|
|
|
7
7
|
app.get('/api/cgv/theaters', async (c) => withEdgeCache(c.req.url, {
|
|
8
8
|
ttlSeconds: 60 * 60 * 24,
|
|
9
9
|
staleWhileRevalidateSeconds: 60 * 5,
|
|
10
|
-
keyPrefix: 'cgv-theaters-
|
|
10
|
+
keyPrefix: 'cgv-theaters-v2',
|
|
11
11
|
}, () => handleCgvFindTheaters(c)));
|
|
12
12
|
app.get('/api/cgv/movies', async (c) => withEdgeCache(c.req.url, {
|
|
13
13
|
ttlSeconds: 60 * 10,
|
|
14
14
|
staleWhileRevalidateSeconds: 60,
|
|
15
|
-
keyPrefix: 'cgv-movies-
|
|
15
|
+
keyPrefix: 'cgv-movies-v2',
|
|
16
16
|
}, () => handleCgvSearchMovies(c)));
|
|
17
17
|
app.get('/api/cgv/timetable', async (c) => withEdgeCache(c.req.url, {
|
|
18
18
|
ttlSeconds: 60 * 3,
|
|
19
19
|
staleWhileRevalidateSeconds: 30,
|
|
20
|
-
keyPrefix: 'cgv-timetable-
|
|
20
|
+
keyPrefix: 'cgv-timetable-v2',
|
|
21
21
|
}, () => handleCgvGetTimetable(c)));
|
|
22
22
|
}
|
|
@@ -7,11 +7,11 @@ export function registerCuRoutes(app) {
|
|
|
7
7
|
app.get('/api/cu/stores', async (c) => withEdgeCache(c.req.url, {
|
|
8
8
|
ttlSeconds: 60 * 60 * 24,
|
|
9
9
|
staleWhileRevalidateSeconds: 60 * 5,
|
|
10
|
-
keyPrefix: 'cu-stores-
|
|
10
|
+
keyPrefix: 'cu-stores-v2',
|
|
11
11
|
}, () => handleCuFindStores(c)));
|
|
12
12
|
app.get('/api/cu/inventory', async (c) => withEdgeCache(c.req.url, {
|
|
13
13
|
ttlSeconds: 60 * 10,
|
|
14
14
|
staleWhileRevalidateSeconds: 60,
|
|
15
|
-
keyPrefix: 'cu-inventory-
|
|
15
|
+
keyPrefix: 'cu-inventory-v2',
|
|
16
16
|
}, () => handleCuCheckInventory(c)));
|
|
17
17
|
}
|
|
@@ -7,16 +7,16 @@ export function registerGs25Routes(app) {
|
|
|
7
7
|
app.get('/api/gs25/stores', async (c) => withEdgeCache(c.req.url, {
|
|
8
8
|
ttlSeconds: 60 * 10,
|
|
9
9
|
staleWhileRevalidateSeconds: 60,
|
|
10
|
-
keyPrefix: 'gs25-stores-
|
|
10
|
+
keyPrefix: 'gs25-stores-v2',
|
|
11
11
|
}, () => handleGs25FindStores(c)));
|
|
12
12
|
app.get('/api/gs25/products', async (c) => withEdgeCache(c.req.url, {
|
|
13
13
|
ttlSeconds: 60 * 3,
|
|
14
14
|
staleWhileRevalidateSeconds: 60,
|
|
15
|
-
keyPrefix: 'gs25-products-
|
|
15
|
+
keyPrefix: 'gs25-products-v2',
|
|
16
16
|
}, () => handleGs25SearchProducts(c)));
|
|
17
17
|
app.get('/api/gs25/inventory', async (c) => withEdgeCache(c.req.url, {
|
|
18
18
|
ttlSeconds: 60 * 2,
|
|
19
19
|
staleWhileRevalidateSeconds: 30,
|
|
20
|
-
keyPrefix: 'gs25-inventory-
|
|
20
|
+
keyPrefix: 'gs25-inventory-v2',
|
|
21
21
|
}, () => handleGs25CheckInventory(c)));
|
|
22
22
|
}
|
|
@@ -7,16 +7,16 @@ export function registerLotteCinemaRoutes(app) {
|
|
|
7
7
|
app.get('/api/lottecinema/theaters', async (c) => withEdgeCache(c.req.url, {
|
|
8
8
|
ttlSeconds: 60 * 60 * 24,
|
|
9
9
|
staleWhileRevalidateSeconds: 60 * 5,
|
|
10
|
-
keyPrefix: 'lottecinema-theaters-
|
|
10
|
+
keyPrefix: 'lottecinema-theaters-v2',
|
|
11
11
|
}, () => handleLotteCinemaFindNearbyTheaters(c)));
|
|
12
12
|
app.get('/api/lottecinema/movies', async (c) => withEdgeCache(c.req.url, {
|
|
13
13
|
ttlSeconds: 60 * 10,
|
|
14
14
|
staleWhileRevalidateSeconds: 60,
|
|
15
|
-
keyPrefix: 'lottecinema-movies-
|
|
15
|
+
keyPrefix: 'lottecinema-movies-v2',
|
|
16
16
|
}, () => handleLotteCinemaListNowShowing(c)));
|
|
17
17
|
app.get('/api/lottecinema/seats', async (c) => withEdgeCache(c.req.url, {
|
|
18
18
|
ttlSeconds: 60 * 3,
|
|
19
19
|
staleWhileRevalidateSeconds: 30,
|
|
20
|
-
keyPrefix: 'lottecinema-seats-
|
|
20
|
+
keyPrefix: 'lottecinema-seats-v2',
|
|
21
21
|
}, () => handleLotteCinemaGetRemainingSeats(c)));
|
|
22
22
|
}
|
|
@@ -1,18 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
* 롯데마트 GET API 라우트 등록
|
|
3
|
-
*/
|
|
4
|
-
import { withEdgeCache } from '../../utils/cache.js';
|
|
5
|
-
import { handleLotteMartDebug, handleLotteMartFindStores, handleLotteMartSearchProducts } from '../lottemartHandlers.js';
|
|
1
|
+
import { handleLotteMartFindStores } from '../lottemartHandlers.js';
|
|
6
2
|
export function registerLotteMartRoutes(app) {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
keyPrefix: 'lottemart-stores-v1',
|
|
11
|
-
}, () => handleLotteMartFindStores(c)));
|
|
12
|
-
app.get('/api/lottemart/products', async (c) => withEdgeCache(c.req.url, {
|
|
13
|
-
ttlSeconds: 60 * 5,
|
|
14
|
-
staleWhileRevalidateSeconds: 60,
|
|
15
|
-
keyPrefix: 'lottemart-products-v1',
|
|
16
|
-
}, () => handleLotteMartSearchProducts(c)));
|
|
17
|
-
app.get('/api/lottemart/debug', (c) => handleLotteMartDebug(c));
|
|
3
|
+
for (const endpoint of ['stores', 'products', 'debug']) {
|
|
4
|
+
app.get(`/api/lottemart/${endpoint}`, handleLotteMartFindStores);
|
|
5
|
+
}
|
|
18
6
|
}
|