Files
portfolio-hub/README.md
T
genichin a111fdd83f feat(REQ-014): add portfolio-create-user-required requirement and implementation
- Add docs/srs/Interface/req-014_portfolio-create-user-required.md with acceptance criteria
- Create docs/batches/bat-004_20260502_portfolio-create-user-required/ delivery batch
- Enhance app/cli/commands/portfolio.py: validate user_id parameter before portfolio creation
- Add comprehensive CLI tests in tests/test_cli.py for portfolio create validation flows
- Update docs/batches/index.md and docs/srs/index.md with new requirement/batch references
- Update README.md documentation
2026-05-02 13:45:46 +00:00

258 lines
8.1 KiB
Markdown

# Portfolio Hub
Portfolio Hub is a FastAPI service that extracts portfolio management from `jsbot-kis` and exposes it through an authenticated HTTP API.
## Current status
This repository is in bootstrap phase.
- FastAPI application entrypoint is available.
- 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 `pfhub_DB_ENABLED=true`.
## Configuration
Copy `.env.example` to `.env` and edit values as needed:
```bash
cp .env.example .env
```
The application loads configuration in this order (higher priority first):
1. Environment variables
2. Variables from `.env` file
3. Default values in code
When deploying to production, set environment variables directly instead of using `.env`.
### Key configuration options
- **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_SERVICE_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**: `pfhub_*_TOKENS` (bearer tokens with role-based access)
For complete configuration options, see `.env.example`.
## Run locally
```bash
python -m venv .venv
source .venv/bin/activate
python -m pip install -e '.[dev]'
# Configure .env
cp .env.example .env
# Start server
uvicorn app.main:app --reload
# In another terminal, run tests
pytest
```
If you need local infrastructure for TimescaleDB, Redis, and Loki, start them with Docker Compose before running the app:
```bash
docker compose -f docker-compose.local-test.yml up -d
docker compose -f docker-compose.local-test.yml ps
```
Default endpoints exposed by `docker-compose.local-test.yml`:
- TimescaleDB: `127.0.0.1:5432`
- Redis: `127.0.0.1:6379` (RedisInsight UI: `127.0.0.1:8001`)
- Loki: `127.0.0.1:3100`
Typical `.env` values for this local setup:
```env
pfhub_DB_ENABLED=true
TIMESCALEDB_HOST=127.0.0.1
TIMESCALEDB_PORT=5432
TIMESCALEDB_USER=portfolio_hub_user
TIMESCALEDB_PASSWORD=change-me
TIMESCALEDB_NAME=portfolio_hub
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
LOKI_HOST=127.0.0.1
LOKI_PORT=3100
```
To stop and remove the local containers:
```bash
docker compose -f docker-compose.local-test.yml down
```
## CLI
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
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 nightly --user alice --ttl-days 30
pfhub service-token issue permanent --user alice --no-expiry
pfhub service-token revoke nightly --force
pfhub portfolio list --user <username>
pfhub portfolio status 005930:tv --recent 10
pfhub portfolio create 005930:tv --symbol 005930 --user <username> --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:
- `init-db`
- `migrate-db`
- `user list`
- `user get`
- `user create`
- `user update`
- `user delete`
- `service-token issue`
- `service-token revoke`
- `portfolio list`
- `portfolio status`
- `portfolio create`
- `portfolio update`
- `portfolio buy`
- `portfolio sell`
- `portfolio invest`
- `portfolio transfer`
- `portfolio reset`
- `portfolio delete`
- `health-check`
Write commands ask for confirmation by default. Use `--force` to skip the prompt for scripts and automation.
Service token issue policy:
- 기본값은 365일 TTL이다.
- `--ttl-days`와 `--no-expiry`는 동시에 사용할 수 없다.
- `--no-expiry`는 관리자 권한에서만 허용된다.
- 토큰 원문은 발급 응답 1회 이후 마스킹된 값으로만 출력/기록된다.
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
pfhub_DB_ENABLED=true pfhub init-db
pfhub_DB_ENABLED=true pfhub health-check
pfhub_DB_ENABLED=true pfhub portfolio list
```
`init-db` will:
- connect to the maintenance database, defaulting to `postgres`
- create the target database if it does not exist
- run Alembic `upgrade head`
`migrate-db` will:
- connect to the target database
- run Alembic `upgrade head`
- apply any future schema revisions in order
This repository now uses Alembic for versioned schema migrations. Future schema changes should be added under `migrations/versions`.
## Docs Sync Guardrail
To keep implementation and specification documents in sync, this repository provides an automated check.
Rules enforced:
- If files under `app/` change, at least one `docs/technical/` document must be updated in the same commit/PR.
- If files under `app/api/` or `app/schemas/` change, `docs/technical/api-contracts.md` must be updated in the same commit/PR.
- If files under `migrations/versions/` change, `docs/technical/persistence-and-migrations.md` must be updated in the same commit/PR.
- Changed migration revision IDs must appear in `docs/technical/persistence-and-migrations.md`.
Enable local pre-commit check:
```bash
pip install -e .[dev]
pre-commit install
```
Run manually:
```bash
python3 scripts/check_docs_sync.py --staged
```
CI check:
- GitHub Actions workflow: `.github/workflows/docs-sync-check.yml`
- Runs automatically on pull requests.
## Authentication model
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
- `admin`: delete, user management, and future operational actions
## User management
User management is exposed through both HTTP API and CLI.
- API base route: `/users`
- Permission: `admin` only
- Supported operations: list, get, create, update, delete
Frontend and app authentication is exposed through `/auth`.
- `POST /auth/login`: username/password login, returns bearer session token
- `GET /auth/tokens`: list current user's issued tokens
- `POST /auth/tokens`: issue a client token for app/script usage
- `DELETE /auth/tokens/{token_id}`: revoke a previously issued token
Service tokens (long-lived integration tokens) are managed via CLI:
```bash
pfhub service-token issue <name> --user <username> [--permission read|trade-write] [--ttl-days N | --no-expiry]
pfhub service-token revoke <name> [--force]
```
Example API calls:
```bash
curl -X GET "http://localhost:8000/users" \
-H "Authorization: Bearer <ADMIN_TOKEN>"
curl -X POST "http://localhost:8000/users" \
-H "Authorization: Bearer <ADMIN_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"username": "alice", "email": "alice@example.com"}'
```
The default `.env.example` includes example tokens for development.
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
1. Apply and verify Alembic migrations against a real PostgreSQL/Timescale instance.
2. Add integration tests for DB-backed portfolio and user flows.
3. Design token-to-user mapping if ownership or user-scoped authorization is needed later.