This commit is contained in:
Michal Humpula
2026-02-15 19:28:19 +01:00
parent 5fbd72b370
commit 940a86ff8c
8 changed files with 1459 additions and 13 deletions

231
doc/API_DESIGN.md Normal file
View File

@@ -0,0 +1,231 @@
# Route-Switcher API Design
## Overview
HTTP REST API with Basic Authentication for Home Assistant integration, exposing state machine state and ping statistics.
## Design Principles
- **Minimal surface area**: Only expose necessary information
- **Simple authentication**: HTTP Basic Auth (no JWT complexity)
- **State-focused**: Centered on state machine state and ping history
- **Home Assistant friendly**: Structured for HA REST integration
- **Opt-in**: API disabled by default
## API Endpoints
### GET /api/state
Returns current state machine state with ping statistics.
**Response:**
```json
{
"state": "Primary",
"primary_stats": {
"success_rate": 95.5,
"failures": 2,
"total_pings": 44,
"last_ping": "Ok"
},
"secondary_stats": {
"success_rate": 98.2,
"failures": 1,
"total_pings": 56,
"last_ping": "Ok"
},
"last_failover": "2024-02-15T10:30:00Z"
}
```
**Fields:**
- `state`: Current state machine state (Boot/Primary/Fallback)
- `primary_stats`: Ping statistics for primary interface
- `secondary_stats`: Ping statistics for secondary interface
- `last_failover`: ISO 8601 timestamp of last failover (null if never)
### POST /api/state
Manually set state machine state.
**Request:**
```json
{
"state": "fallback"
}
```
**Response:**
```json
{
"state": "Fallback",
"previous_state": "Primary",
"primary_stats": { ... },
"secondary_stats": { ... },
"last_failover": "2024-02-15T10:30:00Z"
}
```
**Valid states:** `primary`, `fallback`
## Authentication
HTTP Basic Authentication with username/password configured via environment variables.
**Security considerations:**
- Passwords stored as bcrypt hash
- HTTPS recommended for production
- Local network access only
- No token management (stateless)
## Data Structures
### PingStats
Calculated from state machine ping history (60 entries per interface):
```rust
struct PingStats {
success_rate: f64, // Percentage of successful pings
failures: usize, // Number of failed pings in history
total_pings: usize, // Total pings in history
last_ping: String, // "Ok" or "Failed"
}
```
### StateResponse
```rust
struct StateResponse {
state: String,
primary_stats: PingStats,
secondary_stats: PingStats,
last_failover: Option<String>,
}
```
## Home Assistant Integration
### REST Sensor Configuration
```yaml
sensor:
- platform: rest
name: Route Switcher State
resource: http://route-switcher.local:8080/api/state
username: !secret route_switcher_user
password: !secret route_switcher_pass
value_template: "{{ value_json.state }}"
json_attributes:
- primary_stats
- secondary_stats
- last_failover
- platform: template
sensors:
route_switcher_primary_success_rate:
value_template: "{{ state_attr('sensor.route_switcher_state', 'primary_stats').success_rate | default(0) }}"
unit_of_measurement: "%"
route_switcher_secondary_success_rate:
value_template: "{{ state_attr('sensor.route_switcher_state', 'secondary_stats').success_rate | default(0) }}"
unit_of_measurement: "%"
route_switcher_primary_failures:
value_template: "{{ state_attr('sensor.route_switcher_state', 'primary_stats').failures | default(0) }}"
route_switcher_secondary_failures:
value_template: "{{ state_attr('sensor.route_switcher_state', 'secondary_stats').failures | default(0) }}"
switch:
- platform: rest
name: Route Switcher Control
resource: http://route-switcher.local:8080/api/state
username: !secret route_switcher_user
password: !secret route_switcher_pass
body_on: '{"state": "fallback"}'
body_off: '{"state": "primary"}'
is_on_template: "{{ value_json.state == 'fallback' }}"
```
## Configuration
### Environment Variables
```bash
# API Configuration
API_ENABLED=true
API_BIND_ADDRESS=0.0.0.0
API_PORT=8080
API_USERNAME=admin
API_PASSWORD_HASH=<bcrypt-hash>
# CORS Configuration
API_CORS_ORIGINS=http://homeassistant.local:8123
```
### Password Hash Generation
```bash
# Generate bcrypt hash
echo -n "your-password" | bcrypt
```
## Implementation Details
### Dependencies
```toml
axum = "0.7"
tokio = { version = "1.42", features = ["full"] }
tower = "0.4"
tower-http = { version = "0.5", features = ["cors", "auth"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
chrono = { version = "0.4", features = ["serde"] }
bcrypt = "0.15"
base64 = "0.22"
```
### Architecture
- **API Module**: `src/api.rs` - HTTP server and endpoints
- **State Sharing**: Thread-safe access to state machine and ping history
- **Authentication**: Basic Auth middleware with bcrypt validation
- **Error Handling**: Standardized JSON error responses
- **Integration**: Minimal changes to existing state machine
### Thread Safety
- `Arc<Mutex<StateMachine>>` for shared state access
- Non-blocking async operations
- Minimal locking duration
## Error Handling
Standardized error responses:
```json
{
"error": "Invalid state",
"message": "State must be 'primary' or 'fallback'"
}
```
HTTP Status Codes:
- 200: Success
- 400: Bad Request (invalid state)
- 401: Unauthorized (invalid credentials)
- 500: Internal Server Error
## Security Considerations
- Network access restrictions (local only recommended)
- HTTPS for credential protection
- Rate limiting considerations
- Audit logging for manual state changes
- No configuration exposure (state only)
## Backward Compatibility
- API disabled by default
- No changes to existing CLI functionality
- Service continues without API if disabled
- Graceful degradation on API errors