feat: asset catalog API, 태그 DB 기초, 마이그레이션 3건 및 도메인/테스트 갱신 (2026-05-19)
This commit is contained in:
+21
-11
@@ -68,22 +68,32 @@
|
||||
- Request validation and auth resolution should happen before persistence mutation or response rendering.
|
||||
- The health endpoint remains unauthenticated so infrastructure can probe readiness without a token.
|
||||
|
||||
## Flow: `v1 API — Service-Token Authentication`
|
||||
## Flow: `v1 API — DB Token Authentication`
|
||||
|
||||
1. v1 endpoint receives Bearer token from Authorization header.
|
||||
2. `require_read` resolves the token: checks env tokens first, then DB lookup via `get_token_principal()`.
|
||||
3. If DB token: validates `token_source == "db"` and `token_type == "service"`, else returns HTTP 403.
|
||||
4. `AuthContext` is populated with `user_id`, `token_type`, `permission`, and `runtime_id`.
|
||||
2. `require_read` / `require_admin` resolves the token: checks env tokens first, then DB lookup via `get_token_principal()`.
|
||||
3. Env tokens → HTTP 403 (all /v1 endpoints require DB tokens).
|
||||
4. `AuthContext` is populated with `user_id`, `token_type`, `permission`, `runtime_id`, and `daemon_id`.
|
||||
|
||||
## Flow: `v1 API — GET /v1/assets`
|
||||
|
||||
1. Service-token auth required (see above).
|
||||
2. Daemon-id guard: if `auth.daemon_id is None` → HTTP 403 ("daemon_id not assigned to this token"). NULL tokens are fail-closed.
|
||||
3. `list_portfolios_with_strategies()` fetches only the token owner's portfolios scoped to `managed_by_daemon_id=auth.daemon_id`; `owner_user_id=auth.user_id` prevents cross-user leakage.
|
||||
4. Optional `?status=<value>` query param filters portfolios by status field; `include_disabled=False` by default.
|
||||
5. For portfolios with `active_strategy_id`, `get_strategy_set()` fetches config and JSON-parses `strategy_params`; failures fall back to `{}`.
|
||||
6. Returns `AssetsResponse` with `total` and `assets[]` using field names: `cash`, `quantity`, `broker_type`, `strategy_params`.
|
||||
7. `asset_id` in each asset item equals `portfolio.portfolio_id` (server-generated UUID after BAT-006 migration).
|
||||
1. DB token required; `token_type` drives branching:
|
||||
- `service`: daemon-id guard — if `auth.daemon_id is None` → HTTP 403 (fail-closed). `list_portfolios_with_strategies(owner_user_id=auth.user_id, managed_by_daemon_id=auth.daemon_id, include_disabled=False, market=market)`.
|
||||
- `user`: `list_portfolios_with_strategies(owner_user_id=auth.user_id, include_disabled=True, market=market)`.
|
||||
- `admin`: `list_portfolios_with_strategies(owner_user_id=filter_user_id, include_disabled=True, market=market)` where `filter_user_id = ?user_id param if provided, else auth.user_id`.
|
||||
2. Optional `?status=<value>` query param post-filters portfolios by status field.
|
||||
3. Optional `?market=<code>` query param filters by `assets.markets` array (SQL: `%s = ANY(a.markets)`).
|
||||
4. For portfolios with `active_strategy_id`, `get_strategy_set()` fetches config and JSON-parses `strategy_params`; failures fall back to `{}`.
|
||||
5. Returns `AssetsResponse` with `total` and `assets[]` using field names: `cash`, `quantity`, `broker_type`, `strategy_params`.
|
||||
6. `asset_id` in each asset item equals `portfolio.portfolio_id` (server-generated UUID).
|
||||
|
||||
## Flow: `Admin Asset Catalog CRUD — POST/PATCH /v1/assets`
|
||||
|
||||
1. Admin token required (`require_admin`).
|
||||
2. `POST /v1/assets`: `repo.insert_asset(symbol, markets, display_name, status)` → 201 `AssetCatalogResponse`.
|
||||
3. `PATCH /v1/assets/{asset_id}`: `repo.update_asset(asset_id, markets, display_name, status)` → 200 `AssetCatalogResponse`.
|
||||
4. `PATCH /v1/assets/{asset_id}/deactivate`: `repo.update_asset(asset_id, status="inactive")` → 200 `AssetCatalogResponse`.
|
||||
5. Non-admin callers receive HTTP 403.
|
||||
|
||||
## Flow: `v1 API — GET /v1/user_info`
|
||||
|
||||
|
||||
Reference in New Issue
Block a user