Add admin user management
This commit is contained in:
@@ -259,6 +259,118 @@ Response 204:
|
||||
|
||||
### 5.1 GET /strategy-sets
|
||||
|
||||
## 6. Users
|
||||
|
||||
All user management endpoints require an admin token.
|
||||
|
||||
### 6.1 GET /users
|
||||
|
||||
Request:
|
||||
|
||||
```bash
|
||||
curl -X GET "http://localhost:8000/users" \
|
||||
-H "Authorization: Bearer <ADMIN_TOKEN>"
|
||||
```
|
||||
|
||||
Response 200:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "user-1",
|
||||
"username": "alice",
|
||||
"email": "alice@example.com",
|
||||
"created_at": "2026-04-03T00:00:00Z",
|
||||
"updated_at": "2026-04-03T00:00:00Z"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### 6.2 GET /users/{user_id}
|
||||
|
||||
Request:
|
||||
|
||||
```bash
|
||||
curl -X GET "http://localhost:8000/users/user-1" \
|
||||
-H "Authorization: Bearer <ADMIN_TOKEN>"
|
||||
```
|
||||
|
||||
Response 200:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "user-1",
|
||||
"username": "alice",
|
||||
"email": "alice@example.com",
|
||||
"created_at": "2026-04-03T00:00:00Z",
|
||||
"updated_at": "2026-04-03T00:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
### 6.3 POST /users
|
||||
|
||||
Request:
|
||||
|
||||
```bash
|
||||
curl -X POST "http://localhost:8000/users" \
|
||||
-H "Authorization: Bearer <ADMIN_TOKEN>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"username": "alice",
|
||||
"email": "alice@example.com"
|
||||
}'
|
||||
```
|
||||
|
||||
Response 201:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "user-1",
|
||||
"username": "alice",
|
||||
"email": "alice@example.com",
|
||||
"created_at": "2026-04-03T00:00:00Z",
|
||||
"updated_at": "2026-04-03T00:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
### 6.4 PATCH /users/{user_id}
|
||||
|
||||
Request:
|
||||
|
||||
```bash
|
||||
curl -X PATCH "http://localhost:8000/users/user-1" \
|
||||
-H "Authorization: Bearer <ADMIN_TOKEN>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"username": "alice-updated"
|
||||
}'
|
||||
```
|
||||
|
||||
Response 200:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "user-1",
|
||||
"username": "alice-updated",
|
||||
"email": "alice@example.com",
|
||||
"created_at": "2026-04-03T00:00:00Z",
|
||||
"updated_at": "2026-04-03T00:10:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
### 6.5 DELETE /users/{user_id}
|
||||
|
||||
Request:
|
||||
|
||||
```bash
|
||||
curl -X DELETE "http://localhost:8000/users/user-1" \
|
||||
-H "Authorization: Bearer <ADMIN_TOKEN>"
|
||||
```
|
||||
|
||||
Response 204:
|
||||
|
||||
- No content.
|
||||
|
||||
Request:
|
||||
|
||||
```bash
|
||||
|
||||
+39
-7
@@ -5,17 +5,19 @@
|
||||
This document defines the database schema in an AI-agent-friendly format.
|
||||
|
||||
- Source of truth: Alembic revisions in `migrations/versions`
|
||||
- Current baseline revision: `20260403_0003`
|
||||
- Current baseline revision: `20260403_0004`
|
||||
- Baseline files:
|
||||
- `migrations/versions/20260403_0001_initial_portfolio_schema.py`
|
||||
- `migrations/versions/20260403_0002_strategy_set_columns.py`
|
||||
- `migrations/versions/20260403_0003_portfolio_strategy_refs.py`
|
||||
- `migrations/versions/20260403_0004_users_table.py`
|
||||
|
||||
## 2. Scope
|
||||
|
||||
Schema objects covered by baseline revision:
|
||||
|
||||
- `strategy_sets`
|
||||
- `users`
|
||||
- `portfolios`
|
||||
- `portfolio_ledger`
|
||||
- secondary indexes
|
||||
@@ -30,7 +32,29 @@ Schema objects covered by baseline revision:
|
||||
|
||||
## 4. Tables
|
||||
|
||||
### 4.1 strategy_sets
|
||||
### 4.1 users
|
||||
|
||||
Purpose:
|
||||
|
||||
- Stores admin-managed user records for operational ownership and future identity mapping.
|
||||
|
||||
Columns:
|
||||
|
||||
| Column | Type | Null | Default | Constraints / Notes |
|
||||
|---|---|---|---|---|
|
||||
| id | Text | NO | - | Primary Key |
|
||||
| username | Text | NO | - | Unique username |
|
||||
| email | Text | YES | - | Unique when present |
|
||||
| created_at | DateTime(timezone=True) | NO | NOW() | Creation timestamp |
|
||||
| updated_at | DateTime(timezone=True) | NO | NOW() | Last update timestamp |
|
||||
|
||||
Constraints:
|
||||
|
||||
- Primary key: `id`
|
||||
- Unique: `uq_users_username` on (`username`)
|
||||
- Unique: `uq_users_email` on (`email`)
|
||||
|
||||
### 4.2 strategy_sets
|
||||
|
||||
Purpose:
|
||||
|
||||
@@ -52,7 +76,7 @@ Constraints:
|
||||
- Unique: `uq_strategy_sets_name_config` on (`name`, `config`)
|
||||
- Unique: `uq_strategy_sets_nick` on (`nick`)
|
||||
|
||||
### 4.2 portfolios
|
||||
### 4.3 portfolios
|
||||
|
||||
Purpose:
|
||||
|
||||
@@ -60,7 +84,7 @@ Purpose:
|
||||
|
||||
Current baseline note:
|
||||
|
||||
- The columns below describe the currently migrated schema through revision `20260403_0003`.
|
||||
- The columns below describe the currently migrated schema through revision `20260403_0004`.
|
||||
|
||||
Columns:
|
||||
|
||||
@@ -90,7 +114,7 @@ Constraint notes:
|
||||
- `active_strategy_id` is a nullable logical reference to `strategy_sets.id`
|
||||
- `avabile_strategies` stores zero or more selectable strategy set IDs when present
|
||||
|
||||
### 4.3 portfolio_ledger
|
||||
### 4.4 portfolio_ledger
|
||||
|
||||
Purpose:
|
||||
|
||||
@@ -121,14 +145,21 @@ Constraints:
|
||||
|
||||
## 5. Indexes
|
||||
|
||||
### 5.1 idx_portfolio_ledger_portfolio_time
|
||||
### 5.1 ix_users_created_at
|
||||
|
||||
- Table: `users`
|
||||
- Columns/expressions: (`created_at`)
|
||||
- Unique: no
|
||||
- Intent: speed operational listing by recency.
|
||||
|
||||
### 5.2 idx_portfolio_ledger_portfolio_time
|
||||
|
||||
- Table: `portfolio_ledger`
|
||||
- Columns/expressions: (`portfolio_id`, `recorded_at DESC`)
|
||||
- Unique: no
|
||||
- Intent: fetch recent ledger entries per portfolio efficiently.
|
||||
|
||||
### 5.2 idx_portfolios_broker_status_updated
|
||||
### 5.3 idx_portfolios_broker_status_updated
|
||||
|
||||
- Table: `portfolios`
|
||||
- Columns/expressions: (`broker`, `status`, `updated_at DESC`)
|
||||
@@ -170,3 +201,4 @@ When schema changes:
|
||||
| 20260403_0001 | migrations/versions/20260403_0001_initial_portfolio_schema.py | Initial schema for strategy sets, portfolios, ledger, indexes, optional hypertable |
|
||||
| 20260403_0002 | migrations/versions/20260403_0002_strategy_set_columns.py | Rename `strategy_name`->`name`, `strategy_config`->`config`, add unique `nick`, handle legacy unnamed unique constraint |
|
||||
| 20260403_0003 | migrations/versions/20260403_0003_portfolio_strategy_refs.py | Remove embedded portfolio strategy fields, add `active_strategy_id` and `avabile_strategies` |
|
||||
| 20260403_0004 | migrations/versions/20260403_0004_users_table.py | Add `users` table with unique username/email and created_at index |
|
||||
|
||||
@@ -22,6 +22,7 @@ Primary goals:
|
||||
In scope:
|
||||
|
||||
- Authenticated portfolio read and write operations
|
||||
- Admin-managed user CRUD operations
|
||||
- Role-based access control (read, trade-write, admin)
|
||||
- Optional PostgreSQL/Timescale-backed repository via feature flag
|
||||
- Operational commands for DB bootstrap and migration
|
||||
@@ -121,6 +122,7 @@ Base routes:
|
||||
|
||||
- Health: `/health`
|
||||
- Portfolio: `/portfolios`
|
||||
- Users: `/users`
|
||||
|
||||
### 7.1 Health Endpoint
|
||||
|
||||
@@ -245,6 +247,47 @@ Note: Since strategy sets are identified by (name, config) uniqueness, updating
|
||||
|
||||
- See `docs/api-examples.md` for maintained request/response examples.
|
||||
|
||||
### 7.5 User Endpoints
|
||||
|
||||
All user management endpoints are admin-only.
|
||||
|
||||
#### List Users
|
||||
|
||||
- `GET /users`
|
||||
- Auth: `admin`
|
||||
- Response `200`: array of `UserResponse`
|
||||
|
||||
#### Get User
|
||||
|
||||
- `GET /users/{user_id}`
|
||||
- Auth: `admin`
|
||||
- Response `200`: `UserResponse`
|
||||
- Response `404`: user not found
|
||||
|
||||
#### Create User
|
||||
|
||||
- `POST /users`
|
||||
- Auth: `admin`
|
||||
- Body: `CreateUserRequest`
|
||||
- Response `201`: `UserResponse`
|
||||
- Response `409`: duplicate username or email
|
||||
|
||||
#### Update User
|
||||
|
||||
- `PATCH /users/{user_id}`
|
||||
- Auth: `admin`
|
||||
- Body: `UpdateUserRequest`
|
||||
- Response `200`: `UserResponse`
|
||||
- Response `404`: user not found
|
||||
- Response `409`: duplicate username or email
|
||||
|
||||
#### Delete User
|
||||
|
||||
- `DELETE /users/{user_id}`
|
||||
- Auth: `admin`
|
||||
- Response `204`: no content
|
||||
- Response `404`: user not found
|
||||
|
||||
## 8. Schema Specification
|
||||
|
||||
### 8.1 Request Models
|
||||
@@ -278,6 +321,16 @@ Note: Since strategy sets are identified by (name, config) uniqueness, updating
|
||||
`BuyRequest` / `SellRequest`
|
||||
|
||||
- `quantity: float > 0`
|
||||
|
||||
`CreateUserRequest`
|
||||
|
||||
- `username: str`
|
||||
- `email: str | null`
|
||||
|
||||
`UpdateUserRequest`
|
||||
|
||||
- `username: str | null`
|
||||
- `email: str | null`
|
||||
- `price: float > 0`
|
||||
- `fee: float >= 0`
|
||||
- `tax: float >= 0`
|
||||
|
||||
Reference in New Issue
Block a user