chore: rename CLI to pfhub, update service-token command, and update docs/tests accordingly

This commit is contained in:
2026-04-23 05:25:26 +00:00
parent 02ffd32f2a
commit 5d3884fd4a
4 changed files with 44 additions and 46 deletions
+34 -34
View File
@@ -10,7 +10,7 @@ This repository is in bootstrap phase.
- Token-based authentication and role checks are implemented. - Token-based authentication and role checks are implemented.
- Portfolio and user management API contracts are implemented. - Portfolio and user management API contracts are implemented.
- Core portfolio domain services are ported from `jsbot-kis`. - 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 ## Configuration
@@ -29,14 +29,14 @@ When deploying to production, set environment variables directly instead of usin
### Key configuration options ### Key configuration options
- **DB repository switch**: `PORTFOLIOHUB_DB_ENABLED` (enables DB-backed persistence) - **DB repository switch**: `pfhub_DB_ENABLED` (enables DB-backed persistence)
- **Initial user bootstrap**: `PORTFOLIOHUB_BOOTSTRAP_USERNAME`, `PORTFOLIOHUB_BOOTSTRAP_EMAIL` - **Initial user bootstrap**: `pfhub_BOOTSTRAP_USERNAME`, `pfhub_BOOTSTRAP_EMAIL`
- **Initial user password**: `PORTFOLIOHUB_BOOTSTRAP_PASSWORD` - **Initial user password**: `pfhub_BOOTSTRAP_PASSWORD`
- **Session/token TTL**: `PORTFOLIOHUB_SESSION_TOKEN_TTL_HOURS`, `PORTFOLIOHUB_CLIENT_TOKEN_DEFAULT_TTL_DAYS`, `PORTFOLIOHUB_BOT_TOKEN_DEFAULT_TTL_DAYS` - **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) - **TimescaleDB / PostgreSQL**: `TIMESCALEDB_*` (single source of DB connection settings)
- **Redis**: `REDIS_*` (for caching and sessions) - **Redis**: `REDIS_*` (for caching and sessions)
- **Loki**: `LOKI_*` (for centralized logging) - **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`. 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: Typical `.env` values for this local setup:
```env ```env
PORTFOLIOHUB_DB_ENABLED=true pfhub_DB_ENABLED=true
TIMESCALEDB_HOST=127.0.0.1 TIMESCALEDB_HOST=127.0.0.1
TIMESCALEDB_PORT=5432 TIMESCALEDB_PORT=5432
TIMESCALEDB_USER=portfolio_hub_user TIMESCALEDB_USER=portfolio_hub_user
@@ -95,28 +95,28 @@ docker compose -f docker-compose.local-test.yml down
## CLI ## 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`. The same commands are also available through the shorter alias `phub`.
```bash ```bash
portfoliohub --help pfhub --help
portfoliohub user list pfhub user list
portfoliohub user create --username alice --password password123 --email alice@example.com --force pfhub user create --username alice --password password123 --email alice@example.com --force
portfoliohub user get <user-id> pfhub user get <user-id>
portfoliohub user update <user-id> --username alice-updated --force pfhub user update <user-id> --username alice-updated --force
portfoliohub user delete <user-id> --force pfhub user delete <user-id> --force
portfoliohub bot token issue <bot-id> --name nightly --ttl-days 30 pfhub service-token issue <bot-id> --name nightly --ttl-days 30
portfoliohub bot token issue <bot-id> --name permanent --no-expiry pfhub service-token issue <bot-id> --name permanent --no-expiry
portfoliohub bot token revoke <bot-id> <token-id> --force pfhub service-token revoke <bot-id> <token-id> --force
portfoliohub portfolio list pfhub portfolio list
portfoliohub portfolio status 005930:tv --recent 10 pfhub portfolio status 005930:tv --recent 10
portfoliohub portfolio create 005930:tv --symbol 005930 --cash-balance 10000000 --force pfhub portfolio create 005930:tv --symbol 005930 --cash-balance 10000000 --force
portfoliohub portfolio buy 005930:tv --quantity 10 --price 71000 --fee 5000 --force pfhub portfolio buy 005930:tv --quantity 10 --price 71000 --fee 5000 --force
portfoliohub portfolio sell 005930:tv --quantity 5 --price 72000 --tax 3000 --force pfhub portfolio sell 005930:tv --quantity 5 --price 72000 --tax 3000 --force
portfoliohub portfolio invest 005930:tv --type deposit --amount 50000 --force pfhub portfolio invest 005930:tv --type deposit --amount 50000 --force
portfoliohub portfolio transfer 005930:tv --direction to-buffer --amount 100000 --force pfhub portfolio transfer 005930:tv --direction to-buffer --amount 100000 --force
portfoliohub portfolio reset 005930:tv --force pfhub portfolio reset 005930:tv --force
portfoliohub portfolio delete 005930:tv --keep-ledger --force pfhub portfolio delete 005930:tv --keep-ledger --force
``` ```
Supported commands: Supported commands:
@@ -128,8 +128,8 @@ Supported commands:
- `user create` - `user create`
- `user update` - `user update`
- `user delete` - `user delete`
- `bot token issue` - `service-token issue`
- `bot token revoke` - `service-token revoke`
- `portfolio list` - `portfolio list`
- `portfolio status` - `portfolio status`
- `portfolio create` - `portfolio create`
@@ -151,14 +151,14 @@ Bot token issue policy:
- `--no-expiry`는 관리자 권한에서만 허용된다. - `--no-expiry`는 관리자 권한에서만 허용된다.
- 토큰 원문은 발급 응답 1회 이후 마스킹된 값으로만 출력/기록된다. - 토큰 원문은 발급 응답 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: Initialize a new database explicitly before the first use:
```bash ```bash
PORTFOLIOHUB_DB_ENABLED=true portfoliohub init-db pfhub_DB_ENABLED=true pfhub init-db
PORTFOLIOHUB_DB_ENABLED=true portfoliohub health-check pfhub_DB_ENABLED=true pfhub health-check
PORTFOLIOHUB_DB_ENABLED=true portfoliohub portfolio list pfhub_DB_ENABLED=true pfhub portfolio list
``` ```
`init-db` will: `init-db` will:
@@ -206,7 +206,7 @@ CI check:
## Authentication model ## 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 - `read`: list and status endpoints
- `trade-write`: mutation endpoints such as buy, sell, invest, transfer, reset - `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. 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 ## Planned next steps
+2 -2
View File
@@ -9,7 +9,7 @@ import typer
from rich.console import Console from rich.console import Console
from app.cli.commands.health import register_health_commands 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.portfolio import portfolio_app
from app.cli.commands.strategy import strategy_app from app.cli.commands.strategy import strategy_app
from app.cli.commands.user import user_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(portfolio_app, name="portfolio")
app.add_typer(strategy_app, name="strategy") app.add_typer(strategy_app, name="strategy")
app.add_typer(user_app, name="user") 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) register_health_commands(app)
return app return app
+1 -2
View File
@@ -20,8 +20,7 @@ dependencies = [
] ]
[project.scripts] [project.scripts]
portfoliohub = "app.cli.main:main" pfhub = "app.cli.main:main"
phub = "app.cli.main:main"
[project.optional-dependencies] [project.optional-dependencies]
dev = [ dev = [
+5 -6
View File
@@ -830,7 +830,7 @@ def test_cli_help_exposes_bot_token_commands() -> None:
repo = FakeRepo(_portfolio()) repo = FakeRepo(_portfolio())
runner = build_runner(repo) 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 result.exit_code == 0
assert "issue" in result.stdout assert "issue" in result.stdout
@@ -843,7 +843,7 @@ def test_cli_bot_token_issue_supports_json() -> None:
result = runner.invoke( result = runner.invoke(
runner.app, 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 assert result.exit_code == 0
@@ -861,8 +861,7 @@ def test_cli_bot_token_issue_rejects_conflicting_expiry_options() -> None:
result = runner.invoke( result = runner.invoke(
runner.app, runner.app,
[ [
"bot", "service-token",
"token",
"issue", "issue",
"bot-1", "bot-1",
"--ttl-days", "--ttl-days",
@@ -890,7 +889,7 @@ def test_cli_bot_token_revoke_can_abort() -> None:
result = runner.invoke( result = runner.invoke(
runner.app, runner.app,
["bot", "token", "revoke", "bot-1", token.id], ["service-token", "revoke", "bot-1", token.id],
input="n\n", input="n\n",
) )
@@ -913,7 +912,7 @@ def test_cli_bot_token_revoke_force_succeeds() -> None:
result = runner.invoke( result = runner.invoke(
runner.app, 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 assert result.exit_code == 0