# 사이트·로그인·수집 규칙 원격 변경

서버 v0.3부터 작업 규칙(workflow)을 API로 등록하고 실행합니다. Android v0.2 APK를 그대로 사용할 수 있습니다. 서버가 순서·변수·쿠키와 브라우저를 관리하고, 대상 사이트의 네트워크 연결은 지정한 Android의 Wi-Fi를 통과합니다. 브라우저 JavaScript와 DOM 처리는 중앙 서버에서 실행됩니다.

## 등록 → 실행 → 결과 조회

`GRID_TOKEN`에 관리 API 토큰을 설정합니다. 실제 값은 공개 문서나 셸 기록에 넣지 않습니다. 아래 `example.com` 주소와 선택자는 실제 사이트에 맞춰 바꿔야 합니다. 예제 자체는 해당 사이트의 실제 로그인 API가 아닙니다.

```sh
export GRID_URL=https://request-grid.fika-d.com
read -s GRID_TOKEN
export GRID_TOKEN

# 1. 규칙 등록 (처음은 revision: 1)
curl --fail-with-body "$GRID_URL/v1/workflows/site-items" \
  -X PUT -H "Authorization: Bearer $GRID_TOKEN" \
  -H 'Content-Type: application/json' \
  --data-binary @examples/workflow-http.json

# 2. 실행: 로그인 값은 규칙에 적지 않고 매번 전달
# run-input.private.json: {"nodeId":"region-a","inputs":{"username":"...","password":"..."}}
# 실제 비밀값이 들어간 파일은 0600으로 만들고 Git/공개 폴더에 넣지 않습니다.
curl --fail-with-body "$GRID_URL/v1/workflows/site-items/runs" \
  -X POST -H "Authorization: Bearer $GRID_TOKEN" \
  -H 'Content-Type: application/json' --data-binary @run-input.private.json

# 3. HTTP 202에 반환된 id로 조회
curl --fail-with-body "$GRID_URL/v1/workflow-runs/<id>" \
  -H "Authorization: Bearer $GRID_TOKEN"
```

`state: succeeded`이면 `outputs`에 지정한 항목만 반환됩니다. `running`이면 같은 ID를 다시 조회합니다. POST 응답이 유실되었거나 `uncertain`이면 최근 실행 목록과 대상 사이트를 먼저 확인하며, 같은 작업을 자동으로 다시 실행하지 않습니다. 작업 실행 API에는 `Idempotency-Key` 중복 방지가 없습니다.

Node 예제는 [examples/workflows.ts](../examples/workflows.ts), API 로그인 규칙은 [workflow-http.json](../examples/workflow-http.json), 웹 폼 로그인 규칙은 [workflow-browser.json](../examples/workflow-browser.json)입니다.

## 바뀐 사이트 반영

같은 `id`에서 `revision`을 1씩 올리고 URL·로그인 단계·선택자·출력 필드를 수정해 같은 PUT 주소로 등록합니다. 등록 즉시 다음 실행에 활성 버전을 적용합니다. 실행 중인 작업은 시작 시 선택한 버전과 SHA-256을 유지합니다. 이미 등록된 버전의 내용은 수정할 수 없습니다.

예: v1에서 `.item-title`을 읽다가 사이트가 `.product-name`으로 바뀌면 선택자를 바꾸고 `revision: 2`로 등록합니다. Android 재설치나 서버 재시작은 필요 없습니다. `inputs` 이름이 달라지면 호출 프로그램의 입력도 함께 바꿉니다.

이전 버전으로 되돌리기:

```sh
curl --fail-with-body "$GRID_URL/v1/workflows/site-items/activate" \
  -X POST -H "Authorization: Bearer $GRID_TOKEN" \
  -H 'Content-Type: application/json' --data '{"revision":1}'
```

## API

모두 `Authorization: Bearer <관리 토큰>`으로 인증합니다.

| 메서드 / 경로 | 내용 |
| --- | --- |
| `GET /v1/workflows` | 활성 규칙 목록·입력 이름·버전 |
| `PUT /v1/workflows/:id` | 다음 버전 등록 및 활성화 |
| `GET /v1/workflows/:id?revision=1` | 지정 버전 조회, 생략 시 활성 버전 |
| `POST /v1/workflows/:id/activate` | `{ "revision": 1 }`로 롤백 |
| `POST /v1/workflows/:id/runs` | 실행, HTTP 202와 실행 ID |
| `GET /v1/workflow-runs/:id` | 상태·진행 단계·출력·실행 버전 |
| `GET /v1/workflow-runs` | 최근 50개 실행 |
| `DELETE /v1/workflow-runs/:id` | 취소, 변경 요청 진행 후에는 `uncertain` |

실행 본문은 `inputs`, `nodeId`, `session`, `revision`, `timeoutMs`를 받습니다. 기기를 생략하면 가능한 기기를 선택하고 해당 실행의 세션을 그 기기에 고정합니다. `revision` 생략 시 활성 규칙, 제한 시간은 기본 120초/최대 300초입니다. 동시 실행은 최대 4개이며 초과 시 429입니다. 워크플로는 실행 전체를 자동 재시도하지 않습니다.

## 지원 범위와 저장

- HTTP: 요청 메서드·헤더·JSON/텍스트 본문, 실행 내 쿠키 유지, 응답 JSON 경로 또는 HTML 선택자 추출. `{{input.password}}`, `{{steps.login.extracted}}`로 값을 연결합니다. JSON 경로는 점으로 연결한 객체 키·배열 인덱스입니다. 기본 성공 코드는 2xx이며 `expectStatus`로 바꿀 수 있습니다. 리다이렉트는 자동으로 따라가지 않습니다.
- 브라우저: `goto`, `fill`, `click`, `waitFor`, `extract`. 페이지 이동은 최종 2xx 응답이어야 합니다. 웹 폼 로그인과 JavaScript 렌더링을 처리합니다. 실행마다 새 브라우저 컨텍스트를 만들며 쿠키·로컬 저장소를 다음 실행에 넘기지 않습니다. `session`은 네트워크 기기 고정용입니다.
- 비밀번호·중간 토큰·전체 단계 응답은 실행 메모리에만 둡니다. 실행 이력에는 입력 값을 저장하지 않습니다. 규칙의 리터럴과 최종 `outputs`는 DB에 저장되므로 비밀값을 규칙에 직접 적거나 출력에 포함하지 않습니다. `secret`은 입력 메타데이터이며 출력 값을 자동 가리는 기능은 아닙니다. 최종 출력은 기본 24시간 보존합니다.
- HTTP 응답 1 MiB, 추출/최종 출력 256 KiB, 선택자 결과 1,000개, 규칙 30단계 제한이 있습니다. 파일 다운로드·대용량 스트리밍은 기존 프록시를 사용합니다. 입력이 대입되는 URL은 자동 URL 인코딩하지 않으므로 호출자가 인코딩해야 합니다.
- 모든 연결에 기존 호스트·포트·사설 주소 정책을 적용합니다. Chromium 설치가 필요합니다: `npx playwright install chromium`. Linux에서는 시스템 의존성도 필요하며 Docker 이미지에 함께 설치합니다.
- 서버 재시작 시 진행 중이던 실행은 `uncertain`으로 남기며 자동 재실행하지 않습니다. 2FA·CAPTCHA·생체 인증·앱 간 로그인·대화형 승인·임의 JavaScript 단계는 현재 규칙 엔진 범위에 없습니다. 실제 사이트마다 별도 연동과 검증이 필요합니다.

브라우저 실행은 Chromium sandbox를 활성화합니다. Docker에서는 [Playwright의 공식 안내](https://playwright.dev/docs/docker#crawling-and-scraping)에 따라 사용자 네임스페이스를 허용하는 seccomp 프로필이 필요합니다. 환경이 지원하지 않으면 브라우저 실행은 실패하며 sandbox를 자동 해제하지 않습니다. 이미지는 비루트 사용자로 실행됩니다. 예를 들어 아래처럼 테스트할 수 있습니다.

```sh
mkdir -p artifacts
curl --fail --location https://raw.githubusercontent.com/microsoft/playwright/v1.65.0/utils/docker/seccomp_profile.json \
  --output artifacts/chromium-seccomp.json
docker build -t regional-request-grid:0.3.0 .
docker run --rm --init --security-opt seccomp=artifacts/chromium-seccomp.json \
  regional-request-grid:0.3.0 node scripts/docker-smoke.mjs
```

사이트 규칙 변경과 APK 바이너리 업데이트는 별개입니다. 이 버전은 규칙 원격 변경을 구현하며 APK 자동 설치기는 추가하지 않습니다. 앱의 통신 기능 자체를 바꿀 때는 별도 APK 배포가 필요합니다.
