feat: implement v1 consumer API endpoints with runtime_id support (REQ-004~010)

- Generate runtime_id for service-token issuance (CLI and API)
- Implement GET /v1/assets endpoint with asset view and strategy_params
- Implement GET /v1/user_info endpoint with trading_enabled and broker credentials
- Implement POST /v1/assets/{asset_id}/ledger/trades with idempotency support
- Update auth service to support runtime_id tracking and credential encryption
- Add comprehensive test coverage for v1 endpoints (dcy-002 implementation)
- Update postgres repository for ledger and broker credential queries
- Ensure all tests pass for feature validation
This commit is contained in:
2026-05-13 07:13:03 +00:00
parent 3255d096f1
commit f0a6caa178
7 changed files with 172 additions and 40 deletions
+12 -6
View File
@@ -38,7 +38,8 @@ _SELECT_AUTH_TOKEN = """
expires_at,
last_used_at,
revoked_at,
created_at
created_at,
runtime_id
FROM auth_tokens
"""
@@ -133,6 +134,7 @@ class PostgresPortfolioRepository:
token_hash TEXT NOT NULL UNIQUE,
permission TEXT NOT NULL,
token_type TEXT NOT NULL,
runtime_id TEXT,
expires_at TIMESTAMPTZ,
last_used_at TIMESTAMPTZ,
revoked_at TIMESTAMPTZ,
@@ -140,6 +142,7 @@ class PostgresPortfolioRepository:
)
"""
)
cur.execute("ALTER TABLE auth_tokens ADD COLUMN IF NOT EXISTS runtime_id TEXT")
cur.execute(
"""
CREATE INDEX IF NOT EXISTS idx_auth_tokens_user_created
@@ -300,6 +303,7 @@ class PostgresPortfolioRepository:
last_used_at=row[7] if len(row) > 7 else None,
revoked_at=row[8] if len(row) > 8 else None,
created_at=row[9] if len(row) > 9 else None,
runtime_id=row[10] if len(row) > 10 else None,
)
@staticmethod
@@ -532,6 +536,7 @@ class PostgresPortfolioRepository:
permission: str,
token_type: str,
expires_at: datetime | None = None,
runtime_id: str | None = None,
) -> AuthToken:
self.ensure_schema()
with self._connect().cursor() as cur:
@@ -542,14 +547,14 @@ class PostgresPortfolioRepository:
cur.execute(
"""
INSERT INTO auth_tokens (
id, user_id, name, token_prefix, token_hash, permission, token_type, expires_at
id, user_id, name, token_prefix, token_hash, permission, token_type, expires_at, runtime_id
)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)
RETURNING
id, user_id, name, token_prefix, permission, token_type,
expires_at, last_used_at, revoked_at, created_at
expires_at, last_used_at, revoked_at, created_at, runtime_id
""",
(token_id, user_id, name, token_prefix, token_hash, permission, token_type, expires_at),
(token_id, user_id, name, token_prefix, token_hash, permission, token_type, expires_at, runtime_id),
)
row = cur.fetchone()
return self._row_to_auth_token(row)
@@ -612,7 +617,7 @@ class PostgresPortfolioRepository:
with self._connect().cursor() as cur:
cur.execute(
"""
SELECT t.id, t.user_id, t.permission, t.token_type
SELECT t.id, t.user_id, t.permission, t.token_type, t.runtime_id
FROM auth_tokens t
WHERE token_hash = %s
AND t.revoked_at IS NULL
@@ -629,6 +634,7 @@ class PostgresPortfolioRepository:
user_id=row[1],
permission=row[2],
token_type=row[3],
runtime_id=row[4] if len(row) > 4 else None,
)
def get(self, portfolio_id: str) -> Portfolio: