Phase 8: MVP Polish - COMPLETE (T075-T084)

🎉 MVP v1.0.0 COMPLETE! 🎉

Final polishing phase with comprehensive documentation and enhanced monitoring:

**Enhanced Monitoring:**
- Enhanced health check endpoint with component-level status
  - Database connectivity check (PostgreSQL)
  - Redis connectivity check
  - SDK Bridge connectivity check (gRPC)
  - Overall status (healthy/degraded)
- Metrics endpoint with route counts and feature flags
- Updated root endpoint with metrics link

**Comprehensive Documentation:**
- API Reference (docs/api-reference.md)
  - Complete endpoint documentation
  - Request/response examples
  - Authentication guide
  - Error responses
  - RBAC table
- Deployment Guide (docs/deployment.md)
  - Prerequisites and system requirements
  - Installation instructions
  - Database setup and migrations
  - Production deployment (Windows Service/IIS/Docker)
  - Security hardening
  - Monitoring and alerts
  - Backup and recovery
  - Troubleshooting
- Usage Guide (docs/usage-guide.md)
  - Practical examples with curl
  - Common operations
  - Use case scenarios
  - Python and C# client examples
  - Postman testing guide
  - Best practices
- Release Notes (RELEASE_NOTES.md)
  - Complete MVP feature list
  - Architecture overview
  - Technology stack
  - Installation quick start
  - Testing coverage
  - Security considerations
  - Known limitations
  - Future roadmap

**MVP Deliverables:**
 21 API endpoints
 84 tasks completed
 213 test cases
 3-tier architecture (API + SDK Bridge + GeViServer)
 JWT authentication with RBAC
 Cross-switching control (CORE FEATURE)
 Camera/monitor discovery
 Routing state management
 Audit logging
 Redis caching
 PostgreSQL persistence
 Comprehensive documentation

**Core Functionality:**
- Execute cross-switch (route camera to monitor)
- Clear monitor (remove camera)
- Query routing state (active routes)
- Routing history with pagination
- RBAC enforcement (Operator required for execution)

**Out of Scope (Intentional):**
 Recording management
 Video analytics
 LPR/NPR
 PTZ control
 Live streaming

🚀 Ready for deployment and testing! 🚀

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Geutebruck API Developer
2025-12-09 13:45:32 +01:00
parent aa6f7ec947
commit 36b57db75f
5 changed files with 1679 additions and 4 deletions

305
docs/api-reference.md Normal file
View File

@@ -0,0 +1,305 @@
# Geutebruck Cross-Switching API Reference
## Overview
REST API for Geutebruck GeViScope/GeViSoft cross-switching control. Route cameras to monitors via simple HTTP endpoints.
**Base URL**: `http://localhost:8000`
**API Version**: 1.0.0
**Authentication**: JWT Bearer tokens
## Quick Links
- **Interactive Docs**: http://localhost:8000/docs (Swagger UI)
- **Alternative Docs**: http://localhost:8000/redoc (ReDoc)
- **Health Check**: http://localhost:8000/health
- **Metrics**: http://localhost:8000/metrics
---
## Authentication
### POST /api/v1/auth/login
Authenticate and receive JWT tokens.
**Request:**
```json
{
"username": "admin",
"password": "admin123"
}
```
**Response (200 OK):**
```json
{
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"refresh_token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "bearer",
"expires_in": 3600,
"user": {
"id": "uuid",
"username": "admin",
"role": "administrator"
}
}
```
### POST /api/v1/auth/logout
Logout (blacklist token).
**Headers**: `Authorization: Bearer {access_token}`
**Response (200 OK):**
```json
{
"message": "Successfully logged out"
}
```
### GET /api/v1/auth/me
Get current user information.
**Headers**: `Authorization: Bearer {access_token}`
---
## Cameras
### GET /api/v1/cameras
List all cameras.
**Headers**: `Authorization: Bearer {access_token}`
**Required Role**: Viewer+
**Response (200 OK):**
```json
{
"cameras": [
{
"id": 1,
"name": "Entrance Camera",
"description": "Main entrance",
"has_ptz": true,
"has_video_sensor": true,
"status": "online"
}
],
"total": 1
}
```
### GET /api/v1/cameras/{camera_id}
Get camera details.
**Headers**: `Authorization: Bearer {access_token}`
**Required Role**: Viewer+
---
## Monitors
### GET /api/v1/monitors
List all monitors.
**Headers**: `Authorization: Bearer {access_token}`
**Required Role**: Viewer+
**Response (200 OK):**
```json
{
"monitors": [
{
"id": 1,
"name": "Control Room Monitor 1",
"description": "Main display",
"status": "active",
"current_camera_id": 5
}
],
"total": 1
}
```
### GET /api/v1/monitors/filter/available
Get available (idle) monitors for cross-switching.
**Headers**: `Authorization: Bearer {access_token}`
**Required Role**: Viewer+
---
## Cross-Switching (Core Functionality)
### POST /api/v1/crossswitch
**Execute cross-switch**: Route camera to monitor.
**Headers**: `Authorization: Bearer {access_token}`
**Required Role**: **Operator+** (NOT Viewer)
**Request:**
```json
{
"camera_id": 1,
"monitor_id": 1,
"mode": 0
}
```
**Response (200 OK):**
```json
{
"success": true,
"message": "Successfully switched camera 1 to monitor 1",
"route": {
"id": "uuid",
"camera_id": 1,
"monitor_id": 1,
"executed_at": "2025-12-09T12:00:00Z",
"executed_by": "uuid",
"executed_by_username": "operator",
"is_active": true
}
}
```
### POST /api/v1/crossswitch/clear
**Clear monitor**: Remove camera from monitor.
**Headers**: `Authorization: Bearer {access_token}`
**Required Role**: **Operator+**
**Request:**
```json
{
"monitor_id": 1
}
```
**Response (200 OK):**
```json
{
"success": true,
"message": "Successfully cleared monitor 1",
"monitor_id": 1
}
```
### GET /api/v1/crossswitch/routing
Get current routing state (active camera-to-monitor mappings).
**Headers**: `Authorization: Bearer {access_token}`
**Required Role**: Viewer+
**Response (200 OK):**
```json
{
"routes": [
{
"id": "uuid",
"camera_id": 1,
"monitor_id": 1,
"executed_at": "2025-12-09T12:00:00Z",
"executed_by_username": "operator",
"is_active": true
}
],
"total": 1
}
```
### GET /api/v1/crossswitch/history
Get routing history with pagination.
**Headers**: `Authorization: Bearer {access_token}`
**Required Role**: Viewer+
**Query Parameters:**
- `limit`: Max records (1-1000, default: 100)
- `offset`: Skip records (default: 0)
- `camera_id`: Filter by camera (optional)
- `monitor_id`: Filter by monitor (optional)
---
## Authorization Roles
| Role | Cameras | Monitors | Cross-Switch | Clear Monitor | View Routing |
|------|---------|----------|--------------|---------------|--------------|
| **Viewer** | ✅ Read | ✅ Read | ❌ | ❌ | ✅ Read |
| **Operator** | ✅ Read | ✅ Read | ✅ Execute | ✅ Execute | ✅ Read |
| **Administrator** | ✅ Read | ✅ Read | ✅ Execute | ✅ Execute | ✅ Read |
---
## Error Responses
### 401 Unauthorized
```json
{
"error": "Unauthorized",
"message": "Authentication required"
}
```
### 403 Forbidden
```json
{
"error": "Forbidden",
"message": "Requires operator role or higher"
}
```
### 404 Not Found
```json
{
"error": "Not Found",
"detail": "Camera with ID 999 not found"
}
```
### 500 Internal Server Error
```json
{
"error": "Internal Server Error",
"detail": "Cross-switch operation failed: SDK Bridge connection timeout"
}
```
---
## Rate Limiting
Currently not implemented in MVP. Consider adding in production.
---
## Caching
- **Cameras**: Cached for 60 seconds in Redis
- **Monitors**: Cached for 60 seconds in Redis
- **Routing State**: Not cached (real-time from database)
Use `use_cache=false` query parameter to bypass cache.
---
## Audit Logging
All operations are logged to the `audit_logs` table:
- Authentication attempts (success/failure)
- Cross-switch executions
- Monitor clear operations
Query audit logs via database or add dedicated endpoint in future.

377
docs/deployment.md Normal file
View File

@@ -0,0 +1,377 @@
# Deployment Guide
## Prerequisites
### Required Software
- **Python**: 3.10+ (tested with 3.11)
- **.NET**: .NET 8.0 SDK (for SDK Bridge)
- **.NET Framework**: 4.8 Runtime (for GeViScope SDK)
- **PostgreSQL**: 14+
- **Redis**: 6.0+
- **GeViServer**: GeViScope/GeViSoft installation
### System Requirements
- **OS**: Windows Server 2019+ or Windows 10/11
- **RAM**: 4GB minimum, 8GB recommended
- **Disk**: 10GB free space
- **Network**: Access to GeViServer and PostgreSQL/Redis
---
## Installation
### 1. Clone Repository
```bash
git clone https://git.colsys.tech/COLSYS/geutebruck-api.git
cd geutebruck-api
```
### 2. Configure Environment
Copy `.env.example` to `.env`:
```bash
copy .env.example .env
```
Edit `.env` with your configuration:
```env
# API
API_HOST=0.0.0.0
API_PORT=8000
API_TITLE=Geutebruck Cross-Switching API
API_VERSION=1.0.0
ENVIRONMENT=production
# SDK Bridge
SDK_BRIDGE_HOST=localhost
SDK_BRIDGE_PORT=50051
# GeViServer
GEVISERVER_HOST=your-geviserver-hostname
GEVISERVER_USERNAME=sysadmin
GEVISERVER_PASSWORD=your-password-here
# Database
DATABASE_URL=postgresql+asyncpg://geutebruck:secure_password@localhost:5432/geutebruck_api
# Redis
REDIS_HOST=localhost
REDIS_PORT=6379
# JWT
JWT_SECRET_KEY=generate-a-secure-random-key-here
JWT_ACCESS_TOKEN_EXPIRE_MINUTES=60
JWT_REFRESH_TOKEN_EXPIRE_DAYS=7
# Security
CORS_ORIGINS=["http://localhost:3000"]
# Logging
LOG_FORMAT=json
LOG_LEVEL=INFO
```
**IMPORTANT**: Generate secure `JWT_SECRET_KEY`:
```bash
python -c "import secrets; print(secrets.token_urlsafe(32))"
```
### 3. Install Dependencies
#### Python API
```bash
python -m venv .venv
.venv\Scripts\activate
pip install -r requirements.txt
```
#### SDK Bridge (.NET)
```bash
cd src\sdk-bridge
dotnet restore
dotnet build --configuration Release
```
### 4. Setup Database
Create PostgreSQL database:
```sql
CREATE DATABASE geutebruck_api;
CREATE USER geutebruck WITH PASSWORD 'secure_password';
GRANT ALL PRIVILEGES ON DATABASE geutebruck_api TO geutebruck;
```
Run migrations:
```bash
cd src\api
alembic upgrade head
```
**Default Admin User Created:**
- Username: `admin`
- Password: `admin123`
- **⚠️ CHANGE THIS IMMEDIATELY IN PRODUCTION**
### 5. Verify Redis
```bash
redis-cli ping
# Should return: PONG
```
---
## Running Services
### Development Mode
#### Terminal 1: SDK Bridge
```bash
cd src\sdk-bridge\GeViScopeBridge
dotnet run
```
#### Terminal 2: Python API
```bash
cd src\api
python main.py
```
### Production Mode
#### SDK Bridge (Windows Service)
Create Windows Service using NSSM (Non-Sucking Service Manager):
```bash
nssm install GeViScopeBridge "C:\path\to\dotnet.exe"
nssm set GeViScopeBridge AppDirectory "C:\path\to\geutebruck-api\src\sdk-bridge\GeViScopeBridge"
nssm set GeViScopeBridge AppParameters "run --no-launch-profile"
nssm set GeViScopeBridge DisplayName "GeViScope SDK Bridge"
nssm set GeViScopeBridge Start SERVICE_AUTO_START
nssm start GeViScopeBridge
```
#### Python API (Windows Service/IIS)
**Option 1: Windows Service with NSSM**
```bash
nssm install GeutebruckAPI "C:\path\to\.venv\Scripts\python.exe"
nssm set GeutebruckAPI AppDirectory "C:\path\to\geutebruck-api\src\api"
nssm set GeutebruckAPI AppParameters "main.py"
nssm set GeutebruckAPI DisplayName "Geutebruck API"
nssm set GeutebruckAPI Start SERVICE_AUTO_START
nssm start GeutebruckAPI
```
**Option 2: IIS with FastCGI**
- Install IIS with CGI module
- Install wfastcgi
- Configure IIS to run FastAPI application
- See [Microsoft FastAPI IIS guide](https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/iis/)
**Option 3: Docker (Recommended)**
```bash
docker-compose up -d
```
---
## Health Checks
Verify all components are healthy:
```bash
curl http://localhost:8000/health
```
Expected response:
```json
{
"status": "healthy",
"version": "1.0.0",
"environment": "production",
"components": {
"database": {"status": "healthy", "type": "postgresql"},
"redis": {"status": "healthy", "type": "redis"},
"sdk_bridge": {"status": "healthy", "type": "grpc"}
}
}
```
---
## Security Hardening
### 1. Change Default Credentials
**Admin User:**
```python
from passlib.hash import bcrypt
new_password_hash = bcrypt.hash("your-new-secure-password")
# Update in database: UPDATE users SET password_hash = '...' WHERE username = 'admin';
```
### 2. Configure HTTPS
Use reverse proxy (nginx, IIS) with SSL certificate:
```nginx
server {
listen 443 ssl;
server_name api.your-domain.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
```
### 3. Firewall Rules
- **API**: Allow port 8000 only from trusted networks
- **SDK Bridge**: Port 50051 localhost only
- **PostgreSQL**: Port 5432 localhost only
- **Redis**: Port 6379 localhost only
### 4. Environment Variables
Store sensitive values in secure vault (Azure Key Vault, AWS Secrets Manager, etc.)
---
## Monitoring
### Logs
**Python API:**
- Location: `logs/api.log`
- Format: JSON (structured logging with structlog)
- Rotation: Configure in production
**SDK Bridge:**
- Location: `logs/sdk-bridge.log`
- Format: Serilog JSON
- Rotation: Daily
### Metrics
- Endpoint: `GET /metrics`
- Consider adding Prometheus exporter for production
### Alerts
Configure alerts for:
- Health check failures
- SDK Bridge disconnections
- Database connection failures
- High error rates in audit logs
---
## Backup & Recovery
### Database Backup
```bash
pg_dump -U geutebruck geutebruck_api > backup.sql
```
Restore:
```bash
psql -U geutebruck geutebruck_api < backup.sql
```
### Configuration Backup
Backup `.env` and `appsettings.json` files securely.
---
## Troubleshooting
### SDK Bridge Connection Failed
1. Check GeViServer is reachable
2. Verify credentials in `.env`
3. Check SDK Bridge logs
4. Test SDK connection manually
### Database Connection Issues
1. Verify PostgreSQL is running
2. Check connection string in `.env`
3. Test connection: `psql -U geutebruck geutebruck_api`
### Redis Connection Issues
1. Verify Redis is running: `redis-cli ping`
2. Check Redis host/port in `.env`
### Authentication Failures
1. Check JWT_SECRET_KEY is set
2. Verify token expiration times
3. Check audit logs for failed login attempts
---
## Scaling
### Horizontal Scaling
- Run multiple API instances behind load balancer
- Share Redis and PostgreSQL instances
- Run single SDK Bridge instance per GeViServer
### Vertical Scaling
- Increase database connection pool size
- Increase Redis max connections
- Allocate more CPU/RAM to API process
---
## Maintenance
### Database Migrations
When updating code with new migrations:
```bash
cd src\api
alembic upgrade head
```
### Dependency Updates
```bash
pip install --upgrade -r requirements.txt
dotnet restore
```
### Log Rotation
Configure logrotate (Linux) or Windows Task Scheduler to rotate logs weekly.
---
## Support
For issues or questions:
- **GitHub Issues**: https://github.com/anthropics/claude-code/issues
- **Documentation**: See `docs/` directory
- **API Reference**: http://localhost:8000/docs

483
docs/usage-guide.md Normal file
View File

@@ -0,0 +1,483 @@
# API Usage Guide
Practical examples for using the Geutebruck Cross-Switching API.
---
## Getting Started
### 1. Login
First, authenticate to get your access token:
**Request:**
```bash
curl -X POST http://localhost:8000/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{
"username": "admin",
"password": "admin123"
}'
```
**Response:**
```json
{
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"refresh_token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "bearer",
"expires_in": 3600
}
```
**Save the access token** for subsequent requests.
---
## Common Operations
### Discover Available Cameras
```bash
curl -X GET http://localhost:8000/api/v1/cameras \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
```
**Response:**
```json
{
"cameras": [
{
"id": 1,
"name": "Entrance Camera",
"status": "online",
"has_ptz": true
},
{
"id": 2,
"name": "Parking Lot Camera",
"status": "online",
"has_ptz": false
}
],
"total": 2
}
```
### Discover Available Monitors
```bash
curl -X GET http://localhost:8000/api/v1/monitors \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
```
**Response:**
```json
{
"monitors": [
{
"id": 1,
"name": "Control Room Monitor 1",
"status": "idle",
"current_camera_id": null
},
{
"id": 2,
"name": "Control Room Monitor 2",
"status": "active",
"current_camera_id": 5
}
],
"total": 2
}
```
### Find Available (Idle) Monitors
```bash
curl -X GET http://localhost:8000/api/v1/monitors/filter/available \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
```
Returns only monitors with no camera currently assigned.
---
## Cross-Switching Operations
### Route Camera to Monitor
**⚠️ Requires Operator role or higher**
```bash
curl -X POST http://localhost:8000/api/v1/crossswitch \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"camera_id": 1,
"monitor_id": 1,
"mode": 0
}'
```
**Response:**
```json
{
"success": true,
"message": "Successfully switched camera 1 to monitor 1",
"route": {
"id": "uuid",
"camera_id": 1,
"monitor_id": 1,
"executed_at": "2025-12-09T12:00:00Z",
"executed_by_username": "operator"
}
}
```
### Clear Monitor
**⚠️ Requires Operator role or higher**
```bash
curl -X POST http://localhost:8000/api/v1/crossswitch/clear \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"monitor_id": 1
}'
```
### Get Current Routing State
```bash
curl -X GET http://localhost:8000/api/v1/crossswitch/routing \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
```
**Response:**
```json
{
"routes": [
{
"camera_id": 1,
"monitor_id": 1,
"executed_at": "2025-12-09T12:00:00Z",
"is_active": true
}
],
"total": 1
}
```
### Get Routing History
```bash
curl -X GET "http://localhost:8000/api/v1/crossswitch/history?limit=10&offset=0" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
```
**Filter by camera:**
```bash
curl -X GET "http://localhost:8000/api/v1/crossswitch/history?camera_id=1" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
```
**Filter by monitor:**
```bash
curl -X GET "http://localhost:8000/api/v1/crossswitch/history?monitor_id=1" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
```
---
## Use Case Examples
### Use Case 1: Quick Camera Check
**Scenario**: Operator wants to quickly view entrance camera on their monitor.
**Steps:**
1. Find available monitor
2. Route entrance camera to that monitor
```bash
# Step 1: Find available monitors
curl -X GET http://localhost:8000/api/v1/monitors/filter/available \
-H "Authorization: Bearer $TOKEN"
# Step 2: Route camera 1 to monitor 1
curl -X POST http://localhost:8000/api/v1/crossswitch \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"camera_id": 1, "monitor_id": 1}'
```
### Use Case 2: Monitor Rotation
**Scenario**: Automatically rotate through cameras on a monitor.
**Script (PowerShell):**
```powershell
$token = "YOUR_ACCESS_TOKEN"
$monitor_id = 1
$cameras = @(1, 2, 3, 4) # Camera IDs to rotate
foreach ($camera_id in $cameras) {
# Switch camera
Invoke-RestMethod -Uri "http://localhost:8000/api/v1/crossswitch" `
-Method POST `
-Headers @{ "Authorization" = "Bearer $token" } `
-ContentType "application/json" `
-Body (@{ camera_id = $camera_id; monitor_id = $monitor_id } | ConvertTo-Json)
# Wait 10 seconds
Start-Sleep -Seconds 10
}
```
### Use Case 3: Incident Response
**Scenario**: Security incident detected, switch multiple cameras to control room monitors.
```bash
# Cameras 1-4 to monitors 1-4
for i in {1..4}; do
curl -X POST http://localhost:8000/api/v1/crossswitch \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"camera_id\": $i, \"monitor_id\": $i}"
done
```
### Use Case 4: Audit Trail Review
**Scenario**: Review who accessed which cameras today.
```bash
# Get routing history for today
curl -X GET "http://localhost:8000/api/v1/crossswitch/history?limit=100" \
-H "Authorization: Bearer $TOKEN" \
| jq '.history[] | select(.executed_at | startswith("2025-12-09"))'
```
---
## Python Client Example
```python
import requests
class GeutebruckAPI:
def __init__(self, base_url="http://localhost:8000", username="admin", password="admin123"):
self.base_url = base_url
self.token = None
self.login(username, password)
def login(self, username, password):
"""Authenticate and get token"""
response = requests.post(
f"{self.base_url}/api/v1/auth/login",
json={"username": username, "password": password}
)
response.raise_for_status()
self.token = response.json()["access_token"]
def _headers(self):
return {"Authorization": f"Bearer {self.token}"}
def list_cameras(self):
"""Get all cameras"""
response = requests.get(
f"{self.base_url}/api/v1/cameras",
headers=self._headers()
)
return response.json()
def list_monitors(self):
"""Get all monitors"""
response = requests.get(
f"{self.base_url}/api/v1/monitors",
headers=self._headers()
)
return response.json()
def crossswitch(self, camera_id, monitor_id, mode=0):
"""Execute cross-switch"""
response = requests.post(
f"{self.base_url}/api/v1/crossswitch",
headers=self._headers(),
json={
"camera_id": camera_id,
"monitor_id": monitor_id,
"mode": mode
}
)
return response.json()
def clear_monitor(self, monitor_id):
"""Clear monitor"""
response = requests.post(
f"{self.base_url}/api/v1/crossswitch/clear",
headers=self._headers(),
json={"monitor_id": monitor_id}
)
return response.json()
def get_routing_state(self):
"""Get current routing state"""
response = requests.get(
f"{self.base_url}/api/v1/crossswitch/routing",
headers=self._headers()
)
return response.json()
# Usage Example
api = GeutebruckAPI()
# List cameras
cameras = api.list_cameras()
print(f"Found {cameras['total']} cameras")
# Route camera 1 to monitor 1
result = api.crossswitch(camera_id=1, monitor_id=1)
print(f"Cross-switch: {result['message']}")
# Get routing state
routing = api.get_routing_state()
print(f"Active routes: {routing['total']}")
```
---
## C# Client Example
```csharp
using System;
using System.Net.Http;
using System.Net.Http.Json;
using System.Threading.Tasks;
public class GeutebruckApiClient
{
private readonly HttpClient _client;
private string _accessToken;
public GeutebruckApiClient(string baseUrl = "http://localhost:8000")
{
_client = new HttpClient { BaseAddress = new Uri(baseUrl) };
}
public async Task LoginAsync(string username, string password)
{
var response = await _client.PostAsJsonAsync("/api/v1/auth/login", new
{
username,
password
});
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadFromJsonAsync<LoginResponse>();
_accessToken = result.AccessToken;
_client.DefaultRequestHeaders.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _accessToken);
}
public async Task<CameraListResponse> ListCamerasAsync()
{
var response = await _client.GetAsync("/api/v1/cameras");
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<CameraListResponse>();
}
public async Task<CrossSwitchResponse> ExecuteCrossSwitchAsync(int cameraId, int monitorId, int mode = 0)
{
var response = await _client.PostAsJsonAsync("/api/v1/crossswitch", new
{
camera_id = cameraId,
monitor_id = monitorId,
mode
});
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<CrossSwitchResponse>();
}
}
// Usage
var api = new GeutebruckApiClient();
await api.LoginAsync("admin", "admin123");
var cameras = await api.ListCamerasAsync();
Console.WriteLine($"Found {cameras.Total} cameras");
var result = await api.ExecuteCrossSwitchAsync(cameraId: 1, monitorId: 1);
Console.WriteLine($"Cross-switch: {result.Message}");
```
---
## Testing with Postman
1. **Import Collection**: Import the OpenAPI spec from http://localhost:8000/openapi.json
2. **Set Environment Variable**: Create `access_token` variable
3. **Login**: Run POST /api/v1/auth/login, save token to environment
4. **Test Endpoints**: All subsequent requests will use the token automatically
---
## Troubleshooting
### 401 Unauthorized
**Problem**: Token expired or invalid.
**Solution**: Re-authenticate:
```bash
# Get new token
curl -X POST http://localhost:8000/api/v1/auth/login \
-d '{"username":"admin","password":"admin123"}'
```
### 403 Forbidden
**Problem**: User role insufficient (e.g., Viewer trying to execute cross-switch).
**Solution**: Use account with Operator or Administrator role.
### 404 Not Found
**Problem**: Camera or monitor ID doesn't exist.
**Solution**: List cameras/monitors to find valid IDs.
### 500 Internal Server Error
**Problem**: SDK Bridge communication failure or database error.
**Solution**:
1. Check health endpoint: `/health`
2. Verify SDK Bridge is running
3. Check API logs
---
## Best Practices
1. **Always check health before operations**
2. **Cache camera/monitor lists** (refreshed every 60s)
3. **Handle 401 errors** by re-authenticating
4. **Use refresh tokens** to extend sessions
5. **Log all cross-switch operations** to external system
6. **Implement retry logic** for transient failures
7. **Monitor audit logs** for security events
---
## Next Steps
- Explore interactive documentation: http://localhost:8000/docs
- Review API reference: `docs/api-reference.md`
- Check deployment guide: `docs/deployment.md`
- Review architecture: `docs/architecture.md`