feat: API 키 관리 UI + Tailscale 게이트 유틸 (#46) #47

Merged
xhh merged 1 commit from issue-46-api-key-mgmt into main 2026-04-23 02:06:18 +09:00
Owner

Summary

브라우저에서 API 키를 발급/폐기/회전할 수 있는 운영 페이지 + #44 재사용용 Tailscale 게이트 유틸.

API

  • routers/admin.py + schemas/admin.py/api/admin/keys/* (list/create/delete/rotate)
  • 전체 require_scope("write") 로 보호. 원문 키는 create/rotate 응답에서 단 1회 노출.

Streamlit

  • utils/tailscale_probe.py — 브라우저 JS 프로브 + ADMIN_FORCE_ON=1 로컬 우회, 사이드바 배지
  • utils/api.pypost_json() + delete() 헬퍼 추가
  • pages/7_API_Keys.py — 목록/발급/폐기/회전 (게이트됨)
  • pages/6_Manual_Trigger.py — 동일 게이트 적용

테스트

  • tests/api/test_admin_keys.py 12 케이스 (80 tests pass, 68 → 80)

Test plan

  • ruff clean
  • 80 tests pass
  • 로컬 ADMIN_FORCE_ON=1 uv run streamlit run frontend/streamlit/app.py 에서 API_Keys / Manual_Trigger 페이지 HTTP 200
  • 실제 Tailscale 감지 — #44 배포 시 검증

Closes #46

## Summary 브라우저에서 API 키를 발급/폐기/회전할 수 있는 운영 페이지 + #44 재사용용 Tailscale 게이트 유틸. ### API - `routers/admin.py` + `schemas/admin.py` — `/api/admin/keys/*` (list/create/delete/rotate) - 전체 `require_scope("write")` 로 보호. 원문 키는 create/rotate 응답에서 단 1회 노출. ### Streamlit - `utils/tailscale_probe.py` — 브라우저 JS 프로브 + `ADMIN_FORCE_ON=1` 로컬 우회, 사이드바 배지 - `utils/api.py` — `post_json()` + `delete()` 헬퍼 추가 - `pages/7_API_Keys.py` — 목록/발급/폐기/회전 (게이트됨) - `pages/6_Manual_Trigger.py` — 동일 게이트 적용 ### 테스트 - `tests/api/test_admin_keys.py` 12 케이스 (80 tests pass, 68 → 80) ## Test plan - [x] ruff clean - [x] 80 tests pass - [x] 로컬 `ADMIN_FORCE_ON=1 uv run streamlit run frontend/streamlit/app.py` 에서 API_Keys / Manual_Trigger 페이지 HTTP 200 - [x] 실제 Tailscale 감지 — #44 배포 시 검증 Closes #46
feat: API 키 관리 UI + Tailscale 게이트 유틸 (#46)
All checks were successful
Tests (PR) / pytest (pull_request) Successful in 27s
ae1764e870
브라우저에서 API 키를 발급/폐기/회전할 수 있는 운영 페이지와, #44 에서
재사용할 Tailscale 감지 유틸을 한 번에 정리.

API
---
- `routers/admin.py` + `schemas/admin.py` — `/api/admin/keys/*`
  - GET list (include_revoked 필터)
  - POST create (원문 키 1회 노출, 409 중복)
  - DELETE {name} (soft revoke)
  - POST {name}/rotate (구 키 `<name>#revoked-<id>` 로 rename + soft delete,
    동명 재발급, 스코프 유지)
  - 전체 `require_scope("write")` 로 보호
- `main.py` 에 router 등록

Streamlit
---------
- `utils/tailscale_probe.py` — 브라우저에서 `100.100.100.100` 로 짧은
  fetch → query param 경유로 `st.session_state["tailscale_on"]` 설정.
  `ADMIN_FORCE_ON=1` 환경변수로 로컬 우회. `render_mode_badge()` 로
  사이드바에 🔓/🌐 배지.
- `utils/api.py` 에 `post_json()` + `delete()` 헬퍼 추가 (이전 pages 가
  내부 `_headers` 등 사적 심볼을 찌르는 걸 정리).
- `pages/7_API_Keys.py` — 게이트 후 목록/발급/폐기/회전.
  원문 키는 `st.session_state["last_raw_key"]` 에 담아 재렌더에도
  한 번 더 볼 수 있게. '표시 지우기' 버튼으로 수동 제거.
- `pages/6_Manual_Trigger.py` — 동일한 Tailscale 게이트 적용.

테스트
-----
- `tests/api/test_admin_keys.py` 12 케이스:
  빈 목록 / 발급-목록 / 중복 409 / 잘못된 스코프 422 / soft revoke /
  revoke 2회 404 / 존재 안 함 404 / rotate (원문 변경, 구 이름 접미,
  활성 1개 유지) / 스코프 파라미터 3종.

총 80 tests pass (68 → 80).

#44 구현 시 `tailscale_probe.py` 를 그대로 쓰면 되어 추가 구현 최소화.

Closes #46

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
xhh merged commit 42fc97dd07 into main 2026-04-23 02:06:18 +09:00
xhh deleted branch issue-46-api-key-mgmt 2026-04-23 02:06:18 +09:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
xhh/financial-data-platform!47
No description provided.