Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/workflows/health-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,13 @@ jobs:
`status=${payload.status || 'unknown'}`,
`failed=${failedChecks.length}`,
`degraded=${degradedChecks.length}`,
`failedIds=${failedChecks.map((check) => check.id).join(',') || 'none'}`,
`degradedIds=${degradedChecks.map((check) => check.id).join(',') || 'none'}`,
`run=${runUrl}`,
lines.slice(0, 6).join(' | '),
lines.join(' | '),
].filter(Boolean).join(' ');
fs.writeFileSync('health-check-summary.txt', summary);
fs.appendFileSync(process.env.GITHUB_STEP_SUMMARY, `## Service health checks\n\n${summary}\n\n`);
console.log(summary);
if (failedChecks.length > 0 || payload.status === 'fail') {
process.exitCode = 1;
Expand Down
10 changes: 9 additions & 1 deletion .github/workflows/sync-worker-secrets.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ jobs:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
ZYTE_API_KEY: ${{ secrets.ZYTE_API_KEY }}
OY_RELAY_URL: ${{ secrets.OY_RELAY_URL }}
OY_RELAY_TOKEN: ${{ secrets.OY_RELAY_TOKEN }}
OY_ACCESS_CLIENT_ID: ${{ secrets.OY_ACCESS_CLIENT_ID }}
OY_ACCESS_CLIENT_SECRET: ${{ secrets.OY_ACCESS_CLIENT_SECRET }}
GS25_API_KEY: ${{ secrets.GS25_API_KEY }}
GOOGLE_MAPS_API_KEY: ${{ secrets.GOOGLE_MAPS_API_KEY }}
NAVER_CLIENT_ID: ${{ secrets.NAVER_CLIENT_ID }}
Expand All @@ -45,10 +49,14 @@ jobs:
return 0
fi

echo "$value" | npx wrangler secret put "$name"
printf '%s\n' "$value" | npx wrangler secret put "$name"
}

put_secret_if_set ZYTE_API_KEY "$ZYTE_API_KEY"
put_secret_if_set OY_RELAY_URL "$OY_RELAY_URL"
put_secret_if_set OY_RELAY_TOKEN "$OY_RELAY_TOKEN"
put_secret_if_set OY_ACCESS_CLIENT_ID "$OY_ACCESS_CLIENT_ID"
put_secret_if_set OY_ACCESS_CLIENT_SECRET "$OY_ACCESS_CLIENT_SECRET"
put_secret_if_set GS25_API_KEY "$GS25_API_KEY"
put_secret_if_set GOOGLE_MAPS_API_KEY "$GOOGLE_MAPS_API_KEY"
put_secret_if_set NAVER_CLIENT_ID "$NAVER_CLIENT_ID"
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,10 @@ curl -H "Authorization: Bearer $HEALTH_CHECK_SECRET" \
- `timeoutMs=20000`: 체크별 요청 제한 시간
- `slowThresholdMs=9000`: 지정 시간보다 느린 성공 응답을 degraded로 표시

Health Checks 알림은 모든 실패·저하 서비스 ID를 상세 오류보다 먼저 표시합니다. 푸시 알림은 3,500자로 제한되며 전체 오류와 샘플은 해당 실행의 GitHub Actions 요약에 남습니다. 올리브영 릴레이의 설정 완전성은 `config.oliveyoungRelay`로 확인할 수 있습니다. 이 값은 연결 성공을 뜻하지 않으며, [설정 진단 문서](./docs/oliveyoung-free-relay.md#설정-진단)를 참고하세요.

Zyte 유료 요청은 키 설정 여부와 관계없이 비활성화되어 있습니다. 직접 JSON 요청이 실패하면 원래 HTTP 상태와 오류를 유지합니다.

상태 기준:

- `ok`: 필수 응답 구조와 최소 결과가 정상입니다.
Expand Down
8 changes: 8 additions & 0 deletions docs/oliveyoung-free-relay.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ Cloudflare Worker에서 쓰려면 승인된 별도 연결 경로가 필요합니

이 값은 Worker 바인딩에서 REST 핸들러와 MCP 서비스로 전달됩니다. MCP 도구 사용자 인자로 설정할 수 없습니다. 릴레이가 설정되어 있으면 먼저 릴레이를 호출하고, 없으면 공식 API에 직접 요청합니다. 직접 요청 실패 시 운영자에게 릴레이 설정 안내를 반환합니다. 어느 경로도 Zyte를 호출하지 않습니다.

### 설정 진단

`GET /health` 응답의 `config.oliveyoungRelay`는 URL·토큰 값 없이 설정 여부만 보여 줍니다. `urlConfigured`/`tokenConfigured`는 공백을 제외한 값의 존재, `urlValid`는 실제 전송과 같은 URL 검증 결과입니다. HTTPS와 localhost/127.0.0.1의 HTTP만 허용하며 URL 안의 인증 정보·쿼리·프래그먼트는 거부합니다.

`accessClientIdConfigured`/`accessClientSecretConfigured`는 Access 설정 각각의 존재, `accessConfigured`는 두 값의 완전성을 뜻합니다. `accessPairValid`는 둘 다 미지정이거나 둘 다 유효한 값일 때만 참입니다. 빈 문자열을 명시하면 미지정으로 취급하지 않습니다. `configured`는 유효한 URL·토큰·Access 쌍 조건을 모두 만족한다는 뜻이며, 연결 가능 여부나 브라우저 세션의 정상 여부는 보장하지 않습니다. 실제 복구는 fresh health check로 확인합니다.

GitHub Actions의 `Sync Worker Secrets`는 위 네 OY secrets를 동기화하며, 비어 있는 GitHub secret은 기존 Worker 값을 지우지 않고 건너뜁니다.

## 허용 요청

`POST /v1/oliveyoung/{operation}`에 JSON 본문과 `Authorization: Bearer …`를 보냅니다. 대상 호스트는 `https://www.oliveyoung.co.kr`로 고정하며 임의 URL은 받지 않습니다.
Expand Down
17 changes: 17 additions & 0 deletions docs/release-1.2.2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# 1.2.2 운영 진단과 의존성 유지보수

## 변경

- 무료 직접 요청이 400/403/429로 실패할 때 원본 HTTP 오류를 보존합니다. CU 웹 매장 조회와 GS25 검색의 별도 대체 경로도 원본 오류를 보존하고, CGV 차단 진단에는 원본 상태 코드를 포함합니다. 기존 Zyte 키가 설정되어 있어도 비용 정책 메시지로 원본 오류를 덮지 않습니다. 유료 Zyte 호출 차단은 유지합니다.
- `/health`의 `config.oliveyoungRelay`에서 릴레이 URL·토큰과 선택적 Access 인증 쌍의 설정 상태를 확인할 수 있습니다. 설정이 유효하다는 뜻이며 브라우저가 실제 실행 중이거나 올리브영 조회가 성공한다는 보장은 아닙니다. 비밀 값은 반환하지 않습니다.
- Health Checks 알림에서 모든 실패·저하 체크 ID를 상세 메시지보다 먼저 표시하고 전체 상세를 Actions 요약에 남깁니다.
- Worker secrets 동기화에 네 OY 설정을 포함합니다. GitHub에 값이 없는 설정은 건너뛰므로 수동으로 설정한 Worker secret을 지우지 않습니다.
- Hono, Wrangler, Cloudflare 타입, ESLint, typescript-eslint, Biome, Prettier, tsx, Node 타입 및 호환 범위의 간접 의존성을 갱신합니다.

## 복구 여부와 후속 작업

올리브영은 전용 Mac 표준 계정의 GUI 로그인, Tunnel, Access 및 Worker secrets 연결이 완료되어야 복구됩니다. 진단 개선만으로 업스트림 차단이 해제되지는 않습니다. 2026-09-19 로컬 headed Chromium에서 실제 재고 조회 두 건이 성공했고, 부모 프로세스 SIGKILL 후 추적한 Chrome·crashpad 12개 프로세스의 잔여 수는 0개였습니다. 종료 전 관측 RSS는 약 783MiB였습니다. 이는 장기간 무누수 보장이 아닙니다.

디트릭스 PR #188의 표준 포트 변경은 보류합니다. 2026-09-19 production과 같은 compatibility_date 및 요청으로 임시 인증 Worker에서 비교한 결과, 443은 두 번 모두 10초 timeout, 30443은 한 번 timeout과 한 번 HTTP 200(18개 영화)였습니다. 시험 Worker는 삭제했습니다. 로컬 응답 동일성과 상수 테스트만으로 Worker 장애 해결을 증명할 수 없습니다. 전체 실패 시 503을 성공으로 바꾸거나 동일 시간 예산을 임의로 쪼개어 재시도하지 않습니다.

TypeScript 7은 typescript-eslint의 지원 범위 `>=4.8.4 <6.1.0` 밖입니다. Vitest와 coverage-v8는 함께 5.0.1로 갱신하고 지원되는 Node 24에서 검증합니다. 개발 검증에는 Node 24 LTS를 사용하세요. 기존 Node 25는 Vitest 5의 지원 범위 밖입니다.
47 changes: 47 additions & 0 deletions docs/superpowers/plans/2026-09-19-maintenance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# 운영 진단·무료 경로 유지보수 계획

> **For agentic workers:** Use superpowers:subagent-driven-development for implementation and independent spec/quality review.

**Goal:** 유료 Zyte를 계속 차단하면서 실제 원인과 중계 준비 상태를 드러내고 검증된 유지보수를 배포한다.
**Architecture:** 기존 Worker 및 전용 Mac 한 탭·guard·Tunnel·Access 설계를 유지한다. 사용자 9/19 전체 개선 승인 범위이며 다시 설계 승인을 요구하지 않는다.
**Tech Stack:** TypeScript, Vitest, Cloudflare Workers, macOS LaunchAgent, Playwright Chromium.

## Task 1: 운영 진단

- [x] tests/utils/zyteJsonFallback.test.ts에서 키가 있어도 원본 HttpError가 보존되고 유료 요청이 없는 회귀 검사를 먼저 실행해 실패 확인.
- [x] src/utils/zyteJsonFallback.ts에서 호환 옵션을 제거해 fetchJson으로 직접 전달하며 원본 오류를 그대로 전달. requestByZyte 전역 차단은 유지.
- [x] src/api/configStatus.ts와 관련 tests에서 OY relay URL/token 및 Access 쌍의 존재 여부만 노출. 값은 절대 출력하지 않으며 설정됨과 도달 가능함을 구분.
- [x] .github/workflows/health-checks.yml에서 모든 실패 ID를 앞에 나열하고 전체 상세를 Actions summary에 기록. 알림 길이 제한에도 마지막 서비스가 누락되지 않게 구성.
- [x] .github/workflows/sync-worker-secrets.yml에 OY 4개 비밀 추가. 빈 값은 기존과 같이 건너뜀.
- [x] targeted tests 및 100% coverage 검증.

## Task 2: 디트릭스 검증

- [x] PR #188 diff와 로컬 두 포트 응답 및 임시 인증 Worker 비교. 고정 API만 호출, 비밀은 출력 금지, 시험 뒤 Worker 삭제.
- [x] 실제 원격 개선 확인 시 #188 반영 및 재검증. 실패하면 근거를 기록하고 원인 미해결로 유지.

## Task 3: 의존성

- [x] npm update로 지원 범위 내 패키지 갱신. npm outdated와 peer 범위로 major 호환성 판단.
- [x] npm run check, npm run test:coverage, npm run build, npm audit. major는 지원범위 밖이면 보류.

## Task 4: 중계 설치

- [ ] 전용 표준 계정과 Cloudflare 인증 확보 여부 확인. 인증 전에는 공개하거나 개인 사용자 계정으로 대체하지 않음.
- [ ] 준비되면 기존 설치 스크립트 검토 후 Tunnel/Access/DNS/Worker secrets 연결. 브라우저 회수/강제 종료/메모리와 미인증 거부 검증.
- [ ] 막힐 경우 바로 실행 가능한 설치 계획 및 정확한 남은 작업을 기록.

## Task 5: 완료 검증

- [ ] 독립 spec review, quality review 후 모든 검사 통과.
- [ ] PR 생성·검증·기존 승인 범위 병합·배포·필요시 npm patch 릴리스. 운영 fresh health 및 Daiso MCP 확인.
- [ ] PROJECT.md 기록 및 완료 알림.

## 실행 결과

- Node 24.15.0에서 1,958개 테스트, coverage 네 지표 100%, check/build/audit 통과. 독립 spec/quality 리뷰 승인.
- Vitest 및 coverage-v8 5.0.1 갱신 검증 완료. TypeScript 7은 peer 지원 범위 밖으로 유지.
- 디트릭스 443 시험 실패로 PR #188 반영 보류, 기존 포트 유지. 임시 Worker 삭제 완료.
- Mac 실제 조회 2건 성공, 부모 SIGKILL 후 추적 Chrome/crashpad 12개 잔여 0. 상시 설치는 전용 계정 및 관리자/Cloudflare 인증 대기.

- 추가 점검에서 CU 웹 매장·GS25 검색의 별도 오류 덮기를 제거하고 CGV 상태 코드를 보존했습니다. 회귀 검사와 독립 delta 리뷰를 적용했습니다.
9 changes: 9 additions & 0 deletions docs/superpowers/specs/2026-09-19-maintenance-design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# 운영 진단 유지보수 설계

사용자가 2026-09-19 전체 점검의 후속 개선을 승인했습니다. 기존 Mac 전용 계정·headed Chromium 한 탭·독립 guard·Tunnel·Access 설계를 유지합니다.

원본 오류는 직접 HTTP 계층에서 발생한 그대로 전달합니다. 유료 대체 경로는 계속 차단하며, 비활성 경로의 오류가 관측된 원인을 덮지 않게 합니다. 릴레이 설정 상태는 `/health`에 비밀 값 없이 추가하고 전송 계층의 URL 검증을 재사용합니다. 설정 유효성과 런타임 도달 가능성을 구분합니다. 모든 실패 ID가 알림의 상세 내용보다 앞에 오고 전체 진단은 Actions summary에 보존됩니다.

호환 범위 의존성을 갱신합니다. 디트릭스 포트 변경은 실제 Worker 재현이 성공할 때만 반영합니다. 브라우저 실행·SIGKILL 후 회수 시험으로 기존 안전장치를 다시 확인합니다. 관리자 인증 또는 Cloudflare 권한 없이는 다른 계정으로 우회 설치하지 않고, 독립적으로 가능한 코드 배포를 완료합니다.

완료 조건은 check·coverage 100%·build·audit 및 독립 리뷰 통과, 배포 뒤 실제 config/원본 오류/MCP 확인입니다. 외부 연결과 업스트림 장애의 미해결 부분은 별도로 남깁니다.
Loading
Loading