8000 fix: 세븐일레븐 공개 세션 복구 모드 수정 by hmmhmmhm · Pull Request #343 · hmmhmmhm/daiso-mcp · GitHub
[go: up one dir, main page]

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
10 changes: 9 additions & 1 deletion docs/seveneleven-web-stock-flow-analysis.md
Original file line number Diff line number Diff line change
Expand Up @@ -227,8 +227,16 @@

실제 운영 캐시 우회 검사에서 Mac의 일반 HTTP 요청이 원본 403으로 거절됐습니다. 같은 Mac에서 공식 페이지를 연 브라우저와 헤드리스 브라우저의 상품 API는 200이었으며, 이후 Node HTTP 조회도 200으로 복구됐습니다. 쿠키를 포함하지 않은 후속 Node 대조 요청도 성공했으므로, 쿠키 자체가 반드시 필요했다거나 모든 차단 원인이 쿠키였다고 단정하지 않습니다.

중계는 일반 HTTP를 먼저 사용합니다. 선택 설정 `SEVEN_BROWSER_EXECUTABLE`이 있으면 원본 403일 때만 전용 헤드리스 브라우저로 공식 루트 페이지의 공개 세션을 준비합니다. 개인 프로필이나 계정 로그인을 사용하지 않습니다. 브라우저 회수를 확인한 뒤 HTTP API를 한 번만 재시도하고, 이 추가 API 요청도 기존 호출 원장에 반영합니다. 브라우저는 상시 실행하지 않습니다.
중계는 일반 HTTP를 먼저 사용합니다. 선택 설정 `SEVEN_BROWSER_EXECUTABLE`이 있으면 원본 403일 때만 전용 일반 브라우저 모드로 공식 루트 페이지의 공개 세션을 준비합니다. 개인 프로필이나 계정 로그인을 사용하지 않습니다. 브라우저 회수를 확인한 뒤 HTTP API를 한 번만 재시도하고, 이 추가 API 요청도 기존 호출 원장에 반영합니다. 브라우저는 상시 실행하지 않습니다.

공개 세션은 메모리에 최대 30분 보존하며, 쿠키는 세븐일레븐의 고정 원점과 허용 API 경로에만 전송합니다. 준비 작업은 공유하고 최소 60초 간격을 두며, 준비 실패나 반복 403을 정상으로 바꾸지 않습니다. 일반 HTTP에서 정상인 동안에는 브라우저를 시작하지 않습니다. 세션 값·원문·검색 조건은 로그나 health에 기록하지 않습니다. 인증된 편의점 health의 `sevenSession`은 준비 상태와 만료·재시도 시각만 제공합니다.

운영 설정에는 설치된 전용 Chrome for Testing 실행 파일의 절대 경로를 지정합니다. 편의점 상태 디렉터리의 `seven-browser-owner.json`은 기존 guard의 소유권·회수 규칙을 사용합니다. 마커가 남았을 때 소유권 확인 없이 삭제하지 않습니다. 새로운 정상 웹 세션이 준비돼도 원본 403이 계속되거나 추가 사용자 확인이 요구되면 자동으로 그 확인을 우회하지 않으며 실패를 유지합니다.

## 2026-10-29: 공개 세션 복구의 일반 브라우저 모드

Mac에서 원본 HTTP 403과 헤드리스 공개 세션 준비 실패가 반복됐습니다. 같은 Mac의 전용 일반 브라우저 모드에서는 공식 루트, 브라우저 상품 API, 이어지는 일반 HTTP 상품 API가 모두 200이었습니다. 헤드리스 여부와 정상 모드의 관측 차이는 확인했지만 차단의 내부 기준이나 쿠키의 필수 여부는 확정하지 않습니다.

`SEVEN_BROWSER_EXECUTABLE`을 설정한 운영 중계는 원본 403일 때만 비개인 전용 브라우저를 일반 모드로 실행해 공개 세션을 준비하고, 종료 확인 뒤 기존 HTTP 조회를 1회 재시도합니다. 상시 브라우저나 계정 로그인으로 재고를 조회하지 않습니다. 브라우저가 잠깐 표시될 수 있으며 준비 성공·실패 모두 기존 소유권 감시와 종료 확인을 거칩니다. 세션 공유, 준비 최소 간격, 원장, 30분 메모리 보존, 쿠키 전송 원점·경로 제한은 유지합니다. 준비·원본 재시도 실패는 정상 재고로 바꾸지 않습니다.

감시자가 종료됐는데 `state: launching` 마커만 남으면 자동 시작은 계속 차단합니다. 마커가 존재한다는 사실만으로 프로세스가 살아 있다고 판단하거나 파일을 바로 삭제하지 않습니다. 운영자는 마커 lease 안에서 파일 소유자·0600·정체를 확인하고, 감시 PID 소멸과 전용 브라우저·crashpad의 해당 crashDir 사용 여부를 확인해야 합니다. 전용 실행 파일의 브라우저 환경도 `BREAKPAD_DUMP_LOCATION`이 해당 경로를 참조하는지 확인하되 환경 원문이나 토큰을 출력하지 않습니다. 소멸을 증명할 수 있을 때만 원본 마커를 비공개 위치에 이동 보존하고 정상 준비를 재시도합니다. 소유권이 불확실하면 다른 Chrome을 종료하거나 회수를 생략하지 않습니다.
4 changes: 3 additions & 1 deletion scripts/relay/guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href)
env: browserEnvironment(process.env, crashDir),
host: '127.0.0.1',
executablePath:
process.argv[3] === 'headless' ? process.argv[4] : process.env.OY_BROWSER_EXECUTABLE,
process.argv[3] === 'headless' || process.argv[3] === 'headed'
? process.argv[4]
: process.env.OY_BROWSER_EXECUTABLE,
handleSIGINT: false,
handleSIGTERM: false,
handleSIGHUP: false,
Expand Down
2 changes: 1 addition & 1 deletion scripts/relay/seven-browser-session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export async function prepareSevenBrowserSession(
marker: string,
executable: string,
): Promise<SevenPublicSession> {
const owner = await launchGuardedBrowser(marker, undefined, executable);
const owner = await launchGuardedBrowser(marker, undefined, executable, 'headed');
try {
return await bounded(
(async () => {
Expand Down
10 changes: 6 additions & 4 deletions scripts/relay/supervisor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import { ownedGroup, processSnapshot, type ProcessIdentity } from './ownership.j
export async function launchGuardedBrowser(
markerPath: string,
onEvent?: (reason: string) => void | Promise<void>,
headlessExecutable?: string,
browserExecutable?: string,
browserMode?: 'headless' | 'headed',
): Promise<BrowserOwner> {
const release = await acquireMarkerLease(markerPath);
markerPath = release.markerPath;
Expand All @@ -27,7 +28,7 @@ export async function launchGuardedBrowser(
}
};
try {
return await launchWhileLocked(markerPath, serial, onEvent, headlessExecutable);
return await launchWhileLocked(markerPath, serial, onEvent, browserExecutable, browserMode);
} finally {
starting = false;
await release();
Expand All @@ -37,7 +38,8 @@ async function launchWhileLocked(
markerPath: string,
serial: (action: () => Promise<boolean>) => Promise<boolean>,
onEvent?: (reason: string) => void | Promise<void>,
headlessExecutable?: string,
browserExecutable?: string,
browserMode?: 'headless' | 'headed',
): Promise<BrowserOwner> {
const emit = (reason: string) => {
try {
Expand All @@ -55,7 +57,7 @@ async function launchWhileLocked(
}
const guard = fork(
fileURLToPath(new URL('./guard.ts', import.meta.url)),
[markerPath, ...(headlessExecutable ? ['headless', headlessExecutable] : [])],
[markerPath, ...(browserExecutable ? [browserMode ?? 'headless', browserExecutable] : [])],
{
stdio: ['ignore', 'ignore', 'ignore', 'ipc'],
},
Expand Down
9 changes: 7 additions & 2 deletions tests/relay/seven-browser-session.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,14 @@ beforeEach(() => {
context.cookies.mockResolvedValue([{ name: 'public', value: 'session' }]);
owner.close.mockResolvedValue(undefined);
});
it('비개인 헤드리스 브라우저에서 고정 원점의 쿠키만 준비하고 API 재고는 호출하지 않는다', async () => {
it('비개인 일반 브라우저에서 고정 원점의 쿠키만 준비하고 API 재고는 호출하지 않는다', async () => {
const result = await prepareSevenBrowserSession('/state/seven-marker', '/browser/chrome');
expect(mocks.launch).toHaveBeenCalledWith('/state/seven-marker', undefined, '/browser/chrome');
expect(mocks.launch).toHaveBeenCalledWith(
'/state/seven-marker',
undefined,
'/browser/chrome',
'headed',
);
expect(result).toMatchObject({ cookie: 'public=session', userAgent: 'Browser' });
expect(page.goto).toHaveBeenCalledWith(
'https://new.7-elevenapp.co.kr/',
Expand Down
18 changes: 18 additions & 0 deletions tests/relay/supervisor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,3 +250,21 @@ it('지정한 전용 실행 파일로 헤드리스 세션을 준비하고 기존
);
await owner.close();
});

it('명시한 일반 브라우저 모드에도 전용 실행 파일과 종료 확인을 유지한다', async () => {
const child = fixture();
const pending = launchGuardedBrowser(
'/tmp/test-seven-owner',
undefined,
'/browser/chrome',
'headed',
);
await ready(child);
const owner = await pending;
expect(mocks.fork).toHaveBeenCalledWith(
expect.any(String),
['/tmp/test-seven-owner', 'headed', '/browser/chrome'],
expect.any(Object),
);
await owner.close();
});
Loading
0