chore: rename CLI to pfhub, update service-token command, and update docs/tests accordingly
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# PortfolioHub
|
||||
# Portfolio Hub
|
||||
|
||||
PortfolioHub is a FastAPI service that extracts portfolio management from `jsbot-kis` and exposes it through an authenticated HTTP API.
|
||||
Portfolio Hub is a FastAPI service that extracts portfolio management from `jsbot-kis` and exposes it through an authenticated HTTP API.
|
||||
|
||||
## Current status
|
||||
|
||||
@@ -10,7 +10,7 @@ This repository is in bootstrap phase.
|
||||
- Token-based authentication and role checks are implemented.
|
||||
- Portfolio and user management API contracts are implemented.
|
||||
- Core portfolio domain services are ported from `jsbot-kis`.
|
||||
- Postgres-backed portfolio and user persistence is implemented and gated behind `PORTFOLIOHUB_DB_ENABLED=true`.
|
||||
- Postgres-backed portfolio and user persistence is implemented and gated behind `pfhub_DB_ENABLED=true`.
|
||||
|
||||
## Configuration
|
||||
|
||||
@@ -29,14 +29,14 @@ When deploying to production, set environment variables directly instead of usin
|
||||
|
||||
### Key configuration options
|
||||
|
||||
- **DB repository switch**: `PORTFOLIOHUB_DB_ENABLED` (enables DB-backed persistence)
|
||||
- **Initial user bootstrap**: `PORTFOLIOHUB_BOOTSTRAP_USERNAME`, `PORTFOLIOHUB_BOOTSTRAP_EMAIL`
|
||||
- **Initial user password**: `PORTFOLIOHUB_BOOTSTRAP_PASSWORD`
|
||||
- **Session/token TTL**: `PORTFOLIOHUB_SESSION_TOKEN_TTL_HOURS`, `PORTFOLIOHUB_CLIENT_TOKEN_DEFAULT_TTL_DAYS`, `PORTFOLIOHUB_BOT_TOKEN_DEFAULT_TTL_DAYS`
|
||||
- **DB repository switch**: `pfhub_DB_ENABLED` (enables DB-backed persistence)
|
||||
- **Initial user bootstrap**: `pfhub_BOOTSTRAP_USERNAME`, `pfhub_BOOTSTRAP_EMAIL`
|
||||
- **Initial user password**: `pfhub_BOOTSTRAP_PASSWORD`
|
||||
- **Session/token TTL**: `pfhub_SESSION_TOKEN_TTL_HOURS`, `pfhub_CLIENT_TOKEN_DEFAULT_TTL_DAYS`, `pfhub_BOT_TOKEN_DEFAULT_TTL_DAYS`
|
||||
- **TimescaleDB / PostgreSQL**: `TIMESCALEDB_*` (single source of DB connection settings)
|
||||
- **Redis**: `REDIS_*` (for caching and sessions)
|
||||
- **Loki**: `LOKI_*` (for centralized logging)
|
||||
- **Authentication**: `PORTFOLIOHUB_*_TOKENS` (bearer tokens with role-based access)
|
||||
- **Authentication**: `pfhub_*_TOKENS` (bearer tokens with role-based access)
|
||||
|
||||
For complete configuration options, see `.env.example`.
|
||||
|
||||
@@ -73,7 +73,7 @@ Default endpoints exposed by `docker-compose.local-test.yml`:
|
||||
Typical `.env` values for this local setup:
|
||||
|
||||
```env
|
||||
PORTFOLIOHUB_DB_ENABLED=true
|
||||
pfhub_DB_ENABLED=true
|
||||
TIMESCALEDB_HOST=127.0.0.1
|
||||
TIMESCALEDB_PORT=5432
|
||||
TIMESCALEDB_USER=portfolio_hub_user
|
||||
@@ -95,28 +95,28 @@ docker compose -f docker-compose.local-test.yml down
|
||||
|
||||
## CLI
|
||||
|
||||
The repository now includes a `portfoliohub` CLI that mirrors the main portfolio flows from `jsbot-kis`.
|
||||
The repository now includes a `pfhub` CLI that mirrors the main portfolio flows from `jsbot-kis`.
|
||||
The same commands are also available through the shorter alias `phub`.
|
||||
|
||||
```bash
|
||||
portfoliohub --help
|
||||
portfoliohub user list
|
||||
portfoliohub user create --username alice --password password123 --email alice@example.com --force
|
||||
portfoliohub user get <user-id>
|
||||
portfoliohub user update <user-id> --username alice-updated --force
|
||||
portfoliohub user delete <user-id> --force
|
||||
portfoliohub bot token issue <bot-id> --name nightly --ttl-days 30
|
||||
portfoliohub bot token issue <bot-id> --name permanent --no-expiry
|
||||
portfoliohub bot token revoke <bot-id> <token-id> --force
|
||||
portfoliohub portfolio list
|
||||
portfoliohub portfolio status 005930:tv --recent 10
|
||||
portfoliohub portfolio create 005930:tv --symbol 005930 --cash-balance 10000000 --force
|
||||
portfoliohub portfolio buy 005930:tv --quantity 10 --price 71000 --fee 5000 --force
|
||||
portfoliohub portfolio sell 005930:tv --quantity 5 --price 72000 --tax 3000 --force
|
||||
portfoliohub portfolio invest 005930:tv --type deposit --amount 50000 --force
|
||||
portfoliohub portfolio transfer 005930:tv --direction to-buffer --amount 100000 --force
|
||||
portfoliohub portfolio reset 005930:tv --force
|
||||
portfoliohub portfolio delete 005930:tv --keep-ledger --force
|
||||
pfhub --help
|
||||
pfhub user list
|
||||
pfhub user create --username alice --password password123 --email alice@example.com --force
|
||||
pfhub user get <user-id>
|
||||
pfhub user update <user-id> --username alice-updated --force
|
||||
pfhub user delete <user-id> --force
|
||||
pfhub service-token issue <bot-id> --name nightly --ttl-days 30
|
||||
pfhub service-token issue <bot-id> --name permanent --no-expiry
|
||||
pfhub service-token revoke <bot-id> <token-id> --force
|
||||
pfhub portfolio list
|
||||
pfhub portfolio status 005930:tv --recent 10
|
||||
pfhub portfolio create 005930:tv --symbol 005930 --cash-balance 10000000 --force
|
||||
pfhub portfolio buy 005930:tv --quantity 10 --price 71000 --fee 5000 --force
|
||||
pfhub portfolio sell 005930:tv --quantity 5 --price 72000 --tax 3000 --force
|
||||
pfhub portfolio invest 005930:tv --type deposit --amount 50000 --force
|
||||
pfhub portfolio transfer 005930:tv --direction to-buffer --amount 100000 --force
|
||||
pfhub portfolio reset 005930:tv --force
|
||||
pfhub portfolio delete 005930:tv --keep-ledger --force
|
||||
```
|
||||
|
||||
Supported commands:
|
||||
@@ -128,8 +128,8 @@ Supported commands:
|
||||
- `user create`
|
||||
- `user update`
|
||||
- `user delete`
|
||||
- `bot token issue`
|
||||
- `bot token revoke`
|
||||
- `service-token issue`
|
||||
- `service-token revoke`
|
||||
- `portfolio list`
|
||||
- `portfolio status`
|
||||
- `portfolio create`
|
||||
@@ -151,14 +151,14 @@ Bot token issue policy:
|
||||
- `--no-expiry`는 관리자 권한에서만 허용된다.
|
||||
- 토큰 원문은 발급 응답 1회 이후 마스킹된 값으로만 출력/기록된다.
|
||||
|
||||
The CLI uses the same repository and settings layer as the HTTP API. To target the external TimescaleDB, set `PORTFOLIOHUB_DB_ENABLED=true` and provide `TIMESCALEDB_*` values in the environment or `.env`.
|
||||
The CLI uses the same repository and settings layer as the HTTP API. To target the external TimescaleDB, set `pfhub_DB_ENABLED=true` and provide `TIMESCALEDB_*` values in the environment or `.env`.
|
||||
|
||||
Initialize a new database explicitly before the first use:
|
||||
|
||||
```bash
|
||||
PORTFOLIOHUB_DB_ENABLED=true portfoliohub init-db
|
||||
PORTFOLIOHUB_DB_ENABLED=true portfoliohub health-check
|
||||
PORTFOLIOHUB_DB_ENABLED=true portfoliohub portfolio list
|
||||
pfhub_DB_ENABLED=true pfhub init-db
|
||||
pfhub_DB_ENABLED=true pfhub health-check
|
||||
pfhub_DB_ENABLED=true pfhub portfolio list
|
||||
```
|
||||
|
||||
`init-db` will:
|
||||
@@ -206,7 +206,7 @@ CI check:
|
||||
|
||||
## Authentication model
|
||||
|
||||
PortfolioHub currently supports bearer tokens with three permission levels.
|
||||
pfhub currently supports bearer tokens with three permission levels.
|
||||
|
||||
- `read`: list and status endpoints
|
||||
- `trade-write`: mutation endpoints such as buy, sell, invest, transfer, reset
|
||||
@@ -247,7 +247,7 @@ curl -X POST "http://localhost:8000/users" \
|
||||
|
||||
The default `.env.example` includes example tokens for development.
|
||||
|
||||
When `PORTFOLIOHUB_DB_ENABLED=true` and both `PORTFOLIOHUB_BOOTSTRAP_USERNAME` and `PORTFOLIOHUB_BOOTSTRAP_PASSWORD` are set, the app creates that admin user automatically on startup only if the `users` table is empty. This is intended for first-run bootstrap, not recurring synchronization.
|
||||
When `pfhub_DB_ENABLED=true` and both `pfhub_BOOTSTRAP_USERNAME` and `pfhub_BOOTSTRAP_PASSWORD` are set, the app creates that admin user automatically on startup only if the `users` table is empty. This is intended for first-run bootstrap, not recurring synchronization.
|
||||
|
||||
## Planned next steps
|
||||
|
||||
|
||||
+2
-2
@@ -9,7 +9,7 @@ import typer
|
||||
from rich.console import Console
|
||||
|
||||
from app.cli.commands.health import register_health_commands
|
||||
from app.cli.commands.bot_tokens import bot_app
|
||||
from app.cli.commands.bot_tokens import token_app
|
||||
from app.cli.commands.portfolio import portfolio_app
|
||||
from app.cli.commands.strategy import strategy_app
|
||||
from app.cli.commands.user import user_app
|
||||
@@ -50,7 +50,7 @@ def create_app(
|
||||
app.add_typer(portfolio_app, name="portfolio")
|
||||
app.add_typer(strategy_app, name="strategy")
|
||||
app.add_typer(user_app, name="user")
|
||||
app.add_typer(bot_app, name="bot")
|
||||
app.add_typer(token_app, name="service-token")
|
||||
register_health_commands(app)
|
||||
return app
|
||||
|
||||
|
||||
+1
-2
@@ -20,8 +20,7 @@ dependencies = [
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
portfoliohub = "app.cli.main:main"
|
||||
phub = "app.cli.main:main"
|
||||
pfhub = "app.cli.main:main"
|
||||
|
||||
[project.optional-dependencies]
|
||||
dev = [
|
||||
|
||||
+5
-6
@@ -830,7 +830,7 @@ def test_cli_help_exposes_bot_token_commands() -> None:
|
||||
repo = FakeRepo(_portfolio())
|
||||
runner = build_runner(repo)
|
||||
|
||||
result = runner.invoke(runner.app, ["bot", "token", "--help"])
|
||||
result = runner.invoke(runner.app, ["service-token", "--help"])
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert "issue" in result.stdout
|
||||
@@ -843,7 +843,7 @@ def test_cli_bot_token_issue_supports_json() -> None:
|
||||
|
||||
result = runner.invoke(
|
||||
runner.app,
|
||||
["--format", "json", "bot", "token", "issue", "bot-1", "--name", "nightly"],
|
||||
["--format", "json", "service-token", "issue", "bot-1", "--name", "nightly"],
|
||||
)
|
||||
|
||||
assert result.exit_code == 0
|
||||
@@ -861,8 +861,7 @@ def test_cli_bot_token_issue_rejects_conflicting_expiry_options() -> None:
|
||||
result = runner.invoke(
|
||||
runner.app,
|
||||
[
|
||||
"bot",
|
||||
"token",
|
||||
"service-token",
|
||||
"issue",
|
||||
"bot-1",
|
||||
"--ttl-days",
|
||||
@@ -890,7 +889,7 @@ def test_cli_bot_token_revoke_can_abort() -> None:
|
||||
|
||||
result = runner.invoke(
|
||||
runner.app,
|
||||
["bot", "token", "revoke", "bot-1", token.id],
|
||||
["service-token", "revoke", "bot-1", token.id],
|
||||
input="n\n",
|
||||
)
|
||||
|
||||
@@ -913,7 +912,7 @@ def test_cli_bot_token_revoke_force_succeeds() -> None:
|
||||
|
||||
result = runner.invoke(
|
||||
runner.app,
|
||||
["--format", "json", "bot", "token", "revoke", "bot-1", token.id, "--force"],
|
||||
["--format", "json", "service-token", "revoke", "bot-1", token.id, "--force"],
|
||||
)
|
||||
|
||||
assert result.exit_code == 0
|
||||
|
||||
Reference in New Issue
Block a user