관리자 AI 제공자 연결 기능 추가 (claude·codex·agy·openrouter 토큰 붙여넣기 연결)
Some checks failed
API contract / OpenAPI type drift (push) Failing after 12m57s
Some checks failed
API contract / OpenAPI type drift (push) Failing after 12m57s
관리자 /admin/ai 화면에서 provider 토큰(OAuth 액세스 토큰 또는 API 키)을 발급받아 붙여넣으면 DB에 암호화 저장되고, shared-secret으로 보호되는 게이트웨이 내부 엔드포인트로 push되어 실행 중인 엔진 컨테이너에 즉시 적용된다. NAS처럼 게이트웨이가 컨테이너로 도는 환경에서 CLI·로컬 PC 의존 없이 claude_api·openai·openrouter를 연결할 수 있다. - 게이트웨이: openrouter 어댑터 신설(chat/completions), OAuth 토큰이면 Anthropic Bearer 헤더, /internal/provider-credentials GET/POST와 boot_id 기반 재동기화 - API: app.admin_provider_credential 테이블(idempotent DDL), stdlib HMAC-CTR+MAC 암호화 서비스(PROVIDER_CREDENTIAL_SECRET, 폴백 SESSION_SECRET), /admin/providers CRUD·검증 라우트 - 웹: 제공자 연결 패널(저장·검증·해제, 토큰 힌트만 표시), 엔진 선택에 OpenRouter 추가, api.gen.ts 재생성 - compose: api 서비스에 PROVIDER_CREDENTIAL_SECRET 전달(로컬·NAS)
This commit is contained in:
parent
6831a79ffb
commit
4b45d33142
22 changed files with 2088 additions and 13 deletions
|
|
@ -335,6 +335,35 @@ class EngineClient:
|
|||
# DB 회기 종료 성공을 게이트웨이 정리 실패 때문에 되돌리지는 않는다.
|
||||
return False
|
||||
|
||||
async def provider_credentials_status(self) -> dict[str, Any]:
|
||||
"""게이트웨이 프로세스에 주입된 provider 자격증명 상태(boot_id 포함)."""
|
||||
try:
|
||||
response = await self.client.get("/internal/provider-credentials")
|
||||
response.raise_for_status()
|
||||
payload = response.json()
|
||||
return payload if isinstance(payload, dict) else {}
|
||||
except (httpx.HTTPError, ValueError) as exc:
|
||||
raise EngineError(f"provider credentials status unavailable: {exc}") from exc
|
||||
|
||||
async def push_provider_credentials(
|
||||
self, providers: dict[str, dict[str, str]]
|
||||
) -> dict[str, Any]:
|
||||
"""관리자가 저장한 provider 토큰을 게이트웨이 프로세스 환경에 주입한다."""
|
||||
try:
|
||||
response = await self.client.post(
|
||||
"/internal/provider-credentials",
|
||||
json={"providers": providers},
|
||||
)
|
||||
response.raise_for_status()
|
||||
payload = response.json()
|
||||
return payload if isinstance(payload, dict) else {}
|
||||
except httpx.HTTPStatusError as exc:
|
||||
raise EngineError(
|
||||
f"provider credentials push {exc.response.status_code}: {exc.response.text}"
|
||||
) from exc
|
||||
except (httpx.HTTPError, ValueError) as exc:
|
||||
raise EngineError(f"provider credentials push unavailable: {exc}") from exc
|
||||
|
||||
|
||||
# 앱 전역 싱글톤 (main lifespan 에서 startup/shutdown)
|
||||
engine_client = EngineClient()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue