feat(run-agent): send X-Admin-Token so backend AuthGate accepts CI calls

The backend now has a global AuthGateMiddleware: every data route
(workspaces/lookup, upload-agent, sse/...) requires auth, returning
401 {"detail":"authentication required"} otherwise. CI authenticates with
the service token via the X-Admin-Token header (proxies any user_id).

- run_agent_api.py: read ADMIN_API_TOKEN from env, attach X-Admin-Token to
  both httpx clients (all lookup/upload/sse/action/cancel calls); warn if unset
- run-agent.yml: inject ADMIN_API_TOKEN from Gitea repo secret
- Gitea repo secret ADMIN_API_TOKEN registered
- TEST_WORKFLOW.md / .env.example: document ADMIN_API_TOKEN (.env local, secret in CI)

Verified end-to-end: workspaces/lookup for user_id=jhogyu now resolves
(401 -> 200), e.g. Aug_2026_Test -> 3523fa2b-...

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-20 17:02:12 +09:00
co-authored by Claude Opus 4.8
parent 9d86372325
commit 8dfda00c12
4 changed files with 38 additions and 17 deletions
+10 -2
View File
@@ -78,9 +78,17 @@ class AgentRunner:
self.started_at = time.monotonic()
self.deadline = self.started_at + self.max_runtime
self.sse_client = httpx.Client(timeout=httpx.Timeout(30.0))
# 백엔드 전역 AuthGate 통과용 서비스 토큰 (모든 데이터 라우트에 필수).
# X-Admin-Token 은 어느 user_id 로든 대리 호출을 허용한다. 값은 환경변수로만
# 받는다(코드/로그에 노출 금지) — 로컬은 .env, CI 는 Gitea secret.
admin_token = os.environ.get("ADMIN_API_TOKEN", "")
default_headers = {"X-Admin-Token": admin_token} if admin_token else {}
if not admin_token:
log("WARNING: ADMIN_API_TOKEN 미설정 — 인증이 필요한 백엔드는 401 로 거부됩니다")
self.sse_client = httpx.Client(timeout=httpx.Timeout(30.0), headers=default_headers)
# action/cancel 등 단발 요청은 SSE 스트림과 분리된 클라이언트로 보낸다
self.api_client = httpx.Client(timeout=60.0)
self.api_client = httpx.Client(timeout=60.0, headers=default_headers)
self.events_file = (self.output_dir / "events.jsonl").open("a", encoding="utf-8")