REQ-044: list_catalog_assets 의존성을 require_admin → require_read로 완화. asset 쓰기(POST/PATCH/DELETE)는 require_admin 유지. REQ-045: GET /v1/daemons 신규 엔드포인트 — caller 소유 데몬만 반환(require_read). user_id=None(env 토큰)은 빈 목록 200. DaemonResponse/DaemonListResponse 스키마 추가. 기존 테스트(test_v1.py, test_asset_catalog.py, test_daemon_assignment.py, test_tags_master.py) REQ-044 권한 완화에 맞게 업데이트. test_v1_daemons.py 신규 추가(13 케이스). pytest 472 passed, 0 failed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VHtM5Ws1HKCKQ4JGi6GRb5
4.4 KiB
BAT-019 Design
- Status: confirmed
- Batch: BAT-019
Architecture Summary
이 batch는 두 가지 독립적인 변경으로 구성된다.
-
REQ-044:
GET /v1/assets의 인증 게이트를require_admin→require_read로 완화. 기존AssetCatalogListResponse응답 구조, 필터 파라미터, 라우터 등록은 모두 불변. -
REQ-045:
GET /v1/daemons신규 엔드포인트. caller(auth.user_id) 소유 데몬만 반환.require_read인증. 신규 파일:app/api/v1_daemons.py, 신규 스키마:DaemonResponse,DaemonListResponse.app/main.py에 라우터 등록.
Changed Areas
- Docs:
docs/batches/bat-019_20260627_assets-read-permission-and-daemon-list/ - Modules:
app/api/v1_assets.py(require_admin → require_read, list_catalog_assets만)app/api/v1_daemons.py(신규)app/schemas/v1.py(DaemonResponse, DaemonListResponse 추가)app/main.py(v1_daemons 라우터 include_router 추가)
- Interfaces:
GET /v1/assets: 기존 인터페이스 불변, 인증 게이트만 완화GET /v1/daemons: 신규 엔드포인트
Key Decisions
-
REQ-044 — 권한 완화 범위:
list_catalog_assets(GET /v1/assets) 1개만require_read로 교체. POST /v1/assets, PATCH /{id}, PATCH /{id}/deactivate, POST /{id}/tags, DELETE /{id}/tags/{tid}는require_admin유지. 응답 스키마AssetCatalogListResponse는 불변. -
REQ-045 — 경로:
GET /v1/daemons./v1/user/daemons대신/v1/daemons채택 — 기존/v1/tags,/v1/assets,/v1/portfolios패턴과 일치. -
REQ-045 — 응답 스키마:
DaemonListResponse{daemons: list[DaemonResponse], total: int}.DaemonResponse필드:id(str),daemon_slug(str),display_name(str|None),is_active(bool). 기존TagListResponse{tags: list[TagItem], total: int}패턴과 동일 구조. -
REQ-045 — env/service 토큰(user_id=None) 처리:
auth.user_id가 None이면 빈 목록[]+total: 0을 HTTP 200으로 반환. 이유: 환경 변수 토큰 또는 user_id 없는 서비스 토큰은 특정 사용자 소유 데몬이 없으므로 에러보다 빈 응답이 자연스럽다. 기존/v1/user_info의 env 토큰 처리(403 Service token required)와 다른 접근이며, 데몬 목록은 사용자 식별 없이도 빈 목록을 반환하는 것이 호출자 친화적이다. -
REQ-045 — active 필터: 기본적으로 소유 데몬 전체 반환(active/inactive 모두).
is_active필드로 구분 표시. 이유: caller가 비활성 데몬도 인지해야 할 수 있으며(재활성화 등), 필터는 UI 레이어에서 처리 가능. -
REQ-045 — AuthService 활용:
AuthService.find_candidate_daemons_for_user(user_id)호출. 단, 라우터에서 직접 repository를 호출하는 기존 패턴(v1_assets, v1_tags 등)과 일관성을 위해 라우터에서 직접repo.list_daemons_for_user(user_id)를 호출하는 방식 채택 (service 레이어 DI overhead 없이 기존 패턴 유지). -
REQ-044 테스트 영향: 기존 테스트 중
test_get_v1_assets_user_token_returns_403,test_get_v1_assets_service_token_returns_403는 이번 완화로 동작이 바뀜. — user 토큰(permission="read"): 이제 200이어야 함 → 테스트 업데이트. — service 토큰(permission="trade-write"): trade-write는 read 포함(_has_permission 로직상)이므로 200이어야 함 → 테스트 업데이트. 또한test_v1_assets_rejects_env_token(test_v1.py): env read 토큰도 이제 200이어야 함 → 업데이트.test_v1_assets_returns_asset_list: read 토큰이 이제 403이 아닌 200 → 업데이트.test_v1_assets_market_filter,test_v1_assets_asset_id_is_uuid: 동일.
Edge Cases
user_id=None(env 토큰): 빈 목록 200 반환 (결정 #4).- 데몬 없는 사용자: 빈 목록
[]200 반환. - admin 토큰도 caller 스코프: admin 토큰의
user_id가 None이면 빈 목록, user_id가 있으면 해당 user 소유 데몬만. list_daemons_for_userDB 미활성(db_enabled=False): 빈 목록 반환 또는 InMemory 구현에 따름.
Architecture Impact
- none (라우터 추가는 runtime-flows 수준 변경이 아님; project-structure에 신규 파일 기록 수준)
Reference Doc Update Plan
- project-structure.md: none (파일 추가 수준, 별도 문서화 불필요)
- runtime-flows.md: none