# BAT-002 Design - Status: verified - Batch: BAT-002 ## Architecture Summary - **REQ-010**: `app/api/v1_assets.py`에 admin 전용 엔드포인트 3개(`POST /v1/assets`, `PATCH /v1/assets/{id}`, `PATCH /v1/assets/{id}/deactivate`)를 추가하고, `app/cli/commands/asset.py` 신규 파일로 `pfhub asset` 커맨드 그룹을 구현한다. 내부 로직은 BAT-001에서 구현된 `postgres.py`의 `insert_asset` / `update_asset` / `list_assets`를 직접 호출한다. - **REQ-013**: 현재 service token 전용인 `GET /v1/assets` list_assets 핸들러를 `auth.token_type` 분기로 재작성한다. service/user/admin 각 경로에서 동일한 `list_portfolios_with_strategies` repository 메서드를 호출하되 필터 파라미터를 토큰 타입별로 다르게 설정한다. - **REQ-012**: `PortfolioService.create_portfolio()`에 `repository.get_asset_by_id(asset_id)` 호출을 추가하여 active 상태를 검증한다. CLI에 `--symbol` 옵션을 추가해 symbol→asset_id 조회 경로를 제공한다. - **REQ-015**: 코드 변경 없음. 구현 완료 후 baseline 문서 4종 갱신. ## Changed Areas - Docs: - `docs/data-model.md` — assets 엔티티 정식 추가, portfolios asset_id FK 반영 (REQ-015) - `docs/interface-contract.md` — POST/PATCH /v1/assets 신규, GET /v1/assets 토큰 분기 명세 (REQ-015) - `docs/runtime-flows.md` — GET /v1/assets 토큰 분기 흐름, admin asset CRUD 흐름 (REQ-015) - `docs/project-structure.md` — pfhub asset 커맨드 그룹, 신규 asset.py 모듈 (REQ-015) - Modules: - `app/cli/commands/asset.py` (신규) — pfhub asset create/list/update/deactivate - `app/cli/main.py` — asset_app typer 그룹 등록 - `app/api/v1_assets.py` — admin 엔드포인트 3개 추가 + list_assets 재작성 - `app/schemas/v1.py` — AssetCreateRequest, AssetUpdateRequest, AssetResponse 추가 - `app/services/portfolio_service.py` — create_portfolio catalog 검증 로직 추가 - `app/cli/commands/portfolio.py` — create 커맨드에 `--symbol` 옵션 추가 - `app/repositories/postgres.py` — list_portfolios_with_strategies market 필터 파라미터 추가 - Interfaces: - `POST /v1/assets` — admin token 필요, AssetCreateRequest body, 201 AssetResponse - `PATCH /v1/assets/{asset_id}` — admin token 필요, AssetUpdateRequest body, 200 AssetResponse - `PATCH /v1/assets/{asset_id}/deactivate` — admin token 필요, 200 AssetResponse - `GET /v1/assets` — service/user/admin 토큰 분기, `?market=` 파라미터 지원 ## Key Decisions 1. **REQ-013 분기 구조: `auth.token_type` 필드로 분기** - `service` 토큰: `auth.daemon_id` 추출 → None이면 403 fail-closed (기존 REQ-006/008 동작 유지). list_portfolios_with_strategies(managed_by_daemon_id=auth.daemon_id) - `user` 토큰: list_portfolios_with_strategies(owner_user_id=auth.user_id) - `admin` 토큰: 기본 list_portfolios_with_strategies(owner_user_id=auth.user_id); `?user_id=` 파라미터 시 해당 user id로 필터링 - `?market=` 파라미터: 모든 토큰에서 지원 — list_portfolios_with_strategies에 market 필터 파라미터 추가 - 이유: 기존 daemon 인증 흐름 test_daemon_assignment.py와 호환성 유지, token_type은 이미 TokenPrincipal에 존재 2. **REQ-010 admin 권한 검사: `Permission.ADMIN`으로 require_permission 재사용** - 기존 `app/core/auth.py`의 `require_permission` 의존성 또는 `AuthContext` 확인 패턴 활용 - non-admin이 POST/PATCH /v1/assets 호출 시 403 - 이유: 인증 패턴 일관성 유지, 신규 미들웨어 불필요 3. **REQ-012 검증 위치: `PortfolioService.create_portfolio()` 서비스 레이어** - CLI와 HTTP API 양쪽이 PortfolioService를 통하므로 한 곳에서 커버 - `repository.get_asset_by_id(payload.asset_id)` → None이면 "asset not found" ValueError - asset.status != "active"이면 "asset is inactive" ValueError - `--symbol` 옵션은 CLI에만 추가 — CLI에서 `repository.get_asset_by_symbol(symbol)` 조회 후 asset_id 획득 → CreatePortfolioRequest에 전달 - 이유: 서비스 레이어 검증으로 HTTP/CLI 중복 없음, symbol→id 변환은 CLI 관심사 4. **REQ-010 CLI 구조: 별도 파일 `app/cli/commands/asset.py` 분리** - BAT-001의 `portfolio.py` 패턴과 동일하게 `asset_app = typer.Typer(...)` 생성 - `app/cli/main.py`에 `app.add_typer(asset_app, name="asset")`으로 등록 - 이유: 커맨드 그룹별 파일 분리 — 기존 패턴 일관성 5. **구현 순서: REQ-010 → REQ-013 → REQ-012 → REQ-015** - REQ-010이 `v1_assets.py`에 신규 엔드포인트를 추가하고, REQ-013이 같은 파일의 list_assets를 재작성 — 순서 엄수로 충돌 방지 - REQ-012는 서비스 레이어만 수정 — 독립적이지만 REQ-010 완료 후 테스트 환경 정비 후 진행 ## Edge Cases - REQ-013: admin token + `?user_id` 파라미터에서 해당 user가 존재하지 않는 경우 → 빈 배열 반환 (user 존재 여부 비공개, 404 미사용) - REQ-013: service token + daemon_id=None → 403 (기존 REQ-008 동작 그대로) - REQ-012: `--symbol` 조회 시 해당 symbol asset이 없는 경우 → CLI에서 즉시 오류 출력 후 종료 - REQ-012: portfolio create 시 `get_asset_by_id()` 호출 비용 — 로컬 DB에서 단순 PK 조회로 성능 영향 없음 - REQ-010: deactivate된 asset을 참조하는 기존 portfolio → 기존 portfolio 유지, 신규 create만 REQ-012에서 차단 ## Architecture Impact - runtime-flows: `GET /v1/assets` 분기 구조 신규 (service/user/admin 3경로) - project-structure: `pfhub asset` 커맨드 그룹 신규, `app/cli/commands/asset.py` 모듈 신규 ## Reference Doc Update Plan - `project-structure.md`: `app/cli/commands/asset.py` 추가, `pfhub asset` 커맨드 그룹 추가 (REQ-015) - `runtime-flows.md`: GET /v1/assets 토큰 분기 흐름 교체, admin asset CRUD 흐름 신규 추가 (REQ-015)