Phase 1 Complete: Project Setup & Configuration

Completed Tasks (T001-T010):
-  Project structure created (src/, tests/, docs/, scripts/)
-  Python dependencies defined (requirements.txt)
-  C# SDK Bridge project initialized (.csproj)
-  Configuration template (.env.example)
-  Database migration config (alembic.ini)
-  Code quality tools (pyproject.toml with ruff, black, mypy)
-  Development setup script (setup_dev_environment.ps1)
-  Service startup script (start_services.ps1)
-  Architecture documentation (docs/architecture.md)
-  Revised MVP tasks (tasks-revised-mvp.md - 84 tasks focused on cross-switching)

MVP Scope Refined:
- Focus: Cross-switching control for GSCView viewers
- NO recordings, NO analytics, NO LPR in MVP
- REST API only, no UI needed
- Phase 2: GeViSet configuration management

Ready for Phase 2: SDK Bridge Foundation

🤖 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 08:25:26 +01:00
parent dd2278b39a
commit 733b3b924a
9 changed files with 1533 additions and 0 deletions

49
.env.example Normal file
View File

@@ -0,0 +1,49 @@
# API Configuration
API_HOST=0.0.0.0
API_PORT=8000
API_TITLE=Geutebruck Cross-Switching API
API_VERSION=1.0.0
ENVIRONMENT=development
# GeViScope SDK Bridge
SDK_BRIDGE_HOST=localhost
SDK_BRIDGE_PORT=50051
# GeViServer Connection
GEVISERVER_HOST=localhost
GEVISERVER_USERNAME=sysadmin
GEVISERVER_PASSWORD=masterkey
# Database (PostgreSQL)
DATABASE_URL=postgresql+asyncpg://geutebruck:geutebruck@localhost:5432/geutebruck_api
DATABASE_POOL_SIZE=20
DATABASE_MAX_OVERFLOW=10
# Redis
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_DB=0
REDIS_PASSWORD=
REDIS_MAX_CONNECTIONS=50
# JWT Authentication
JWT_SECRET_KEY=change-this-to-a-secure-random-key-in-production
JWT_ALGORITHM=HS256
JWT_ACCESS_TOKEN_EXPIRE_MINUTES=60
JWT_REFRESH_TOKEN_EXPIRE_DAYS=7
# Logging
LOG_LEVEL=INFO
LOG_FORMAT=json
# Security
ALLOWED_HOSTS=*
CORS_ORIGINS=http://localhost:3000,http://localhost:8080
# Cache Settings
CACHE_CAMERA_LIST_TTL=60
CACHE_MONITOR_LIST_TTL=60
# Rate Limiting
RATE_LIMIT_ENABLED=true
RATE_LIMIT_PER_MINUTE=60

102
alembic.ini Normal file
View File

@@ -0,0 +1,102 @@
# A generic, single database configuration.
[alembic]
# path to migration scripts
script_location = src/api/migrations
# template used to generate migration files
file_template = %%(year)d%%(month).2d%%(day).2d_%%(hour).2d%%(minute).2d_%%(rev)s_%%(slug)s
# sys.path path, will be prepended to sys.path if present.
prepend_sys_path = .
# timezone to use when rendering the date within the migration file
# as well as the filename.
# If specified, requires the python-dateutil library that can be
# installed by adding `alembic[tz]` to the pip requirements
# string value is passed to dateutil.tz.gettz()
# leave blank for localtime
# timezone =
# max length of characters to apply to the
# "slug" field
# truncate_slug_length = 40
# set to 'true' to run the environment during
# the 'revision' command, regardless of autogenerate
# revision_environment = false
# set to 'true' to allow .pyc and .pyo files without
# a source .py file to be detected as revisions in the
# versions/ directory
# sourceless = false
# version location specification; This defaults
# to src/api/migrations/versions. When using multiple version
# directories, initial revisions must be specified with --version-path.
# The path separator used here should be the separator specified by "version_path_separator" below.
# version_locations = %(here)s/bar:%(here)s/bat:src/api/migrations/versions
# version path separator; As mentioned above, this is the character used to split
# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep.
# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas.
# Valid values for version_path_separator are:
#
# version_path_separator = :
# version_path_separator = ;
# version_path_separator = space
version_path_separator = os # Use os.pathsep. Default configuration used for new projects.
# the output encoding used when revision files
# are written from script.py.mako
# output_encoding = utf-8
# Database URL (override from environment variable)
sqlalchemy.url = postgresql+asyncpg://geutebruck:geutebruck@localhost:5432/geutebruck_api
[post_write_hooks]
# post_write_hooks defines scripts or Python functions that are run
# on newly generated revision scripts. See the documentation for further
# detail and examples
# format using "black" - use the console_scripts runner, against the "black" entrypoint
# hooks = black
# black.type = console_scripts
# black.entrypoint = black
# black.options = -l 79 REVISION_SCRIPT_FILENAME
# Logging configuration
[loggers]
keys = root,sqlalchemy,alembic
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = WARN
handlers = console
qualname =
[logger_sqlalchemy]
level = WARN
handlers =
qualname = sqlalchemy.engine
[logger_alembic]
level = INFO
handlers =
qualname = alembic
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic
[formatter_generic]
format = %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S

497
docs/architecture.md Normal file
View File

@@ -0,0 +1,497 @@
# Geutebruck Cross-Switching API - Architecture
**Version**: 1.0.0 (MVP)
**Last Updated**: 2025-12-08
**Status**: In Development
---
## Overview
The Geutebruck Cross-Switching API provides a modern REST API for controlling video routing between cameras (video inputs) and monitors/viewers (video outputs) in Geutebruck surveillance systems. The system acts as a bridge between the native GeViScope/GeViSoft SDK and modern web/mobile applications.
**Core Functionality**:
- 🔐 User authentication with JWT tokens
- 📹 Camera discovery and management
- 🖥️ Monitor/viewer discovery and status
- 🔀 Cross-switching operations (route camera to monitor)
- 📊 Routing state tracking and audit logging
---
## System Architecture
### High-Level Architecture
```
┌─────────────────┐
│ Client Apps │ (Postman, curl, custom apps)
└────────┬────────┘
│ HTTP/REST
┌─────────────────┐
│ FastAPI Server │ (Python 3.11)
│ Port: 8000 │
└────────┬────────┘
┌────┴────┬───────────┬──────────┐
│ │ │ │
▼ ▼ ▼ ▼
┌────────┐ ┌────────┐ ┌────────┐ ┌─────────┐
│ PostgreSQL│ │ Redis │ │SDK Bridge│ │Auth/JWT │
│ Port:5432│ │Port:6379│ │Port:50051│ │ Service │
└─────────┘ └────────┘ └────┬───┘ └─────────┘
│ gRPC
┌──────────────┐
│ GeViScope │
│ SDK (.NET) │
└──────┬───────┘
│ TCP/IP
┌──────────────┐
│ GeViServer │
│ Port: 7700+ │
└──────┬───────┘
┌────────┴────────┐
▼ ▼
┌──────────┐ ┌──────────┐
│ Cameras │ │ GSCView │
│ (Inputs) │ │ Viewers │
└──────────┘ └──────────┘
```
---
## Component Details
### 1. FastAPI Server (Python)
**Purpose**: REST API layer handling HTTP requests, authentication, and business logic
**Technology Stack**:
- Python 3.11+
- FastAPI (web framework)
- SQLAlchemy (ORM)
- Pydantic (validation)
- PyJWT (authentication)
**Key Responsibilities**:
- Accept HTTP REST requests from clients
- Authenticate users and generate JWT tokens
- Validate request data
- Communicate with SDK Bridge via gRPC
- Store routing state in PostgreSQL
- Cache camera/monitor lists in Redis
- Audit log all operations
- Return HTTP responses to clients
**Port**: 8000
---
### 2. SDK Bridge (C# .NET 8.0)
**Purpose**: gRPC service that wraps the GeViScope SDK, translating between modern gRPC and legacy SDK
**Technology Stack**:
- C# .NET 8.0
- Grpc.AspNetCore
- GeViScope SDK (.NET Framework 4.8 DLL)
- Serilog (logging)
**Key Responsibilities**:
- Connect to GeViServer using GeViScope SDK
- Enumerate cameras (GetFirstVideoInput / GetNextVideoInput)
- Enumerate monitors (GetFirstVideoOutput / GetNextVideoOutput)
- Execute cross-switching (CrossSwitch action)
- Clear monitors (ClearVideoOutput action)
- Translate SDK errors to gRPC status codes
- Maintain connection health with retry logic
**Why Separate Service?**:
- ✅ Isolates SDK crashes from Python API
- ✅ Enables independent scaling
- ✅ Clear separation of concerns (SDK complexity vs API logic)
- ✅ Type-safe gRPC communication
- ✅ Can run on different machines if needed
**Port**: 50051 (gRPC)
---
### 3. PostgreSQL Database
**Purpose**: Persistent storage for users, routing state, and audit logs
**Schema**:
```sql
users:
- id (UUID, primary key)
- username (unique)
- password_hash (bcrypt)
- role (viewer, operator, administrator)
- created_at, updated_at
crossswitch_routes:
- id (UUID, primary key)
- camera_id (int)
- monitor_id (int)
- switched_at (timestamp)
- switched_by_user_id (UUID, FK to users)
audit_logs:
- id (UUID, primary key)
- user_id (UUID, FK to users)
- action (string)
- target (string)
- timestamp (timestamp)
- details (JSON)
```
**Port**: 5432
---
### 4. Redis
**Purpose**: Session storage, caching, and future pub/sub for events
**Usage**:
- **Session Storage**: JWT tokens and user sessions
- **Caching**: Camera list (60s TTL), monitor list (60s TTL)
- **Future**: Pub/sub for real-time routing updates
**Port**: 6379
---
### 5. GeViScope SDK & GeViServer
**GeViServer**:
- Backend service managing surveillance system
- Handles actual video routing
- Controls GSCView viewers
- Manages camera inputs and outputs
**GeViScope SDK**:
- .NET Framework 4.8 DLL (GeViProcAPINET_4_0.dll)
- Provides C# wrapper for GeViServer communication
- Uses action-based message passing
- State query pattern for enumeration
**Ports**: 7700, 7701, 7703
---
## Data Flow
### 1. Authentication Flow
```
Client → POST /api/v1/auth/login
{ username: "admin", password: "secret" }
FastAPI validates credentials
Hash password with bcrypt
Query PostgreSQL for user
Generate JWT token (1hr expiry)
Store session in Redis
Client ← { access_token: "eyJ...", token_type: "bearer" }
```
### 2. Camera Discovery Flow
```
Client → GET /api/v1/cameras
Header: Authorization: Bearer eyJ...
FastAPI validates JWT
Check Redis cache for camera list
↓ (cache miss)
gRPC call to SDK Bridge: ListCameras()
SDK Bridge → GeViScope SDK
→ CSQGetFirstVideoInput()
→ CSQGetNextVideoInput() (loop)
SDK Bridge ← Camera list
FastAPI ← gRPC response
Store in Redis (60s TTL)
Client ← { cameras: [
{ id: 1, name: "Camera 1", has_ptz: false },
{ id: 2, name: "Front Gate", has_ptz: true }
]}
```
### 3. Cross-Switching Flow
```
Client → POST /api/v1/crossswitch
{ camera_id: 7, monitor_id: 3, mode: 0 }
FastAPI validates JWT (requires operator role)
Validate camera_id and monitor_id exist
gRPC call to SDK Bridge: ExecuteCrossSwitch(7, 3, 0)
SDK Bridge → GeViScope SDK
→ SendMessage("CrossSwitch(7, 3, 0)")
GeViServer executes cross-switch
SDK Bridge ← Success confirmation
FastAPI stores route in PostgreSQL
FastAPI logs to audit_logs table
Client ← { success: true, message: "Camera 7 routed to monitor 3" }
```
---
## Security Architecture
### Authentication & Authorization
**Authentication**: JWT (JSON Web Tokens)
- Access tokens: 1 hour lifetime
- Refresh tokens: 7 days lifetime (future)
- Tokens stored in Redis for quick invalidation
- Bcrypt password hashing (cost factor: 12)
**Authorization**: Role-Based Access Control (RBAC)
| Role | Permissions |
|------|------------|
| **Viewer** | Read cameras, Read monitors, Read routing state |
| **Operator** | Viewer + Execute cross-switch, Clear monitors |
| **Administrator** | Operator + User management, Configuration |
### API Security
- ✅ HTTPS enforced in production (TLS 1.2+)
- ✅ CORS configured for allowed origins
- ✅ Rate limiting (60 requests/minute per IP)
- ✅ JWT secret key from environment (not hardcoded)
- ✅ Database credentials in environment variables
- ✅ No stack traces exposed to clients
- ✅ Audit logging for all operations
---
## Scalability Considerations
### Current Architecture (MVP)
- Single FastAPI instance
- Single SDK Bridge instance
- Single GeViServer connection
### Future Horizontal Scaling
**FastAPI Layer**:
- ✅ Stateless design enables multiple instances
- ✅ Load balancer in front (nginx/HAProxy)
- ✅ Shared PostgreSQL and Redis
**SDK Bridge Layer**:
- ⚠️ Limited by GeViServer connection capacity
- Consider: Connection pooling pattern
- Consider: Multiple SDK Bridge instances if needed
**Database Layer**:
- PostgreSQL read replicas for camera/monitor queries
- Redis Cluster for high availability
---
## Error Handling
### SDK Bridge Error Translation
| SDK Error | gRPC Status | HTTP Status |
|-----------|-------------|-------------|
| Connection Failed | UNAVAILABLE | 503 Service Unavailable |
| Invalid Channel | INVALID_ARGUMENT | 400 Bad Request |
| Permission Denied | PERMISSION_DENIED | 403 Forbidden |
| Timeout | DEADLINE_EXCEEDED | 504 Gateway Timeout |
| Unknown | INTERNAL | 500 Internal Server Error |
### Retry Logic
- SDK Bridge connection: 3 attempts with exponential backoff
- gRPC calls from FastAPI: 2 attempts with 1s delay
- Transient errors logged but not exposed to client
---
## Monitoring & Observability
### Logging
**FastAPI**:
- Structured JSON logs (Structlog)
- Log levels: DEBUG, INFO, WARNING, ERROR
- Correlation IDs for request tracing
**SDK Bridge**:
- Serilog with file and console sinks
- Separate logs for SDK communication
### Metrics (Future)
**Prometheus Endpoint**: `/metrics`
- Request count by endpoint
- Request latency (p50, p95, p99)
- Active cross-switch operations
- gRPC call success/failure rates
- Cache hit/miss rates
### Health Checks
**Endpoint**: `GET /api/v1/health`
Returns:
```json
{
"status": "healthy",
"components": {
"database": "up",
"redis": "up",
"sdk_bridge": "up"
},
"timestamp": "2025-12-08T15:30:00Z"
}
```
---
## Deployment Architecture
### Development Environment
```
Localhost:
- PostgreSQL (Docker or native)
- Redis (Docker or native)
- SDK Bridge (.NET)
- FastAPI (uvicorn --reload)
- GeViServer (C:\GEVISOFT\GeViServer.exe)
```
### Production Environment (Windows Server)
```
Windows Server 2016+:
- GeViServer (native Windows service)
- SDK Bridge (Windows service via NSSM)
- PostgreSQL (Docker or native)
- Redis (Docker or native)
- FastAPI (Docker or uvicorn behind nginx)
- Nginx (reverse proxy with SSL termination)
```
### Network Requirements
- Port 8000: FastAPI (HTTPS in production)
- Port 50051: SDK Bridge gRPC (internal only)
- Port 5432: PostgreSQL (internal only)
- Port 6379: Redis (internal only)
- Port 7700-7703: GeViServer (internal only)
---
## Technology Choices Rationale
### Why Python FastAPI?
- ✅ Modern async Python framework
- ✅ Automatic OpenAPI documentation
- ✅ Fast development cycle
- ✅ Rich ecosystem (SQLAlchemy, Pydantic)
- ✅ Easy to expand with new features
### Why C# SDK Bridge?
- ✅ GeViScope SDK is .NET Framework 4.8
- ✅ gRPC provides type-safe communication
- ✅ Isolates SDK complexity
- ✅ Can run on separate machine if needed
### Why PostgreSQL?
- ✅ Mature, reliable, ACID compliant
- ✅ JSON support for flexible audit logs
- ✅ Good performance for relational data
### Why Redis?
- ✅ Fast in-memory caching
- ✅ Session storage
- ✅ Future: pub/sub for events
### Why gRPC (not REST for SDK Bridge)?
- ✅ Type-safe protocol buffers
- ✅ Efficient binary protocol
- ✅ Streaming support (future)
- ✅ Language-agnostic
---
## Future Enhancements (Phase 2)
1. **GeViSet Configuration Management**
- Retrieve action mappings from GeViServer
- Modify configurations via API
- Export/import to CSV
- Push configurations back to server
2. **Real-Time Event Stream**
- WebSocket endpoint for routing changes
- Redis pub/sub for event distribution
- Monitor status change notifications
3. **PTZ Camera Control**
- Pan/tilt/zoom commands
- Preset positions
- Tour sequences
4. **Multi-Tenancy**
- Organization/tenant isolation
- Per-tenant GeViServer connections
5. **Advanced Analytics**
- Routing history reports
- Usage patterns
- Performance metrics
---
## Development Workflow
1. **Setup**: `.\scripts\setup_dev_environment.ps1`
2. **Start Services**: `.\scripts\start_services.ps1`
3. **Database Migrations**: `alembic upgrade head`
4. **Run Tests**: `pytest tests/ -v`
5. **Code Quality**: `ruff check src/api` + `black src/api`
6. **API Docs**: http://localhost:8000/docs
---
## References
- **FastAPI Documentation**: https://fastapi.tiangolo.com
- **gRPC .NET**: https://grpc.io/docs/languages/csharp/
- **GeViScope SDK**: See `docs/SDK_INTEGRATION_LESSONS.md`
- **SQLAlchemy**: https://docs.sqlalchemy.org
- **Pydantic**: https://docs.pydantic.dev
---
**Document Version**: 1.0
**Architecture Status**: ✅ Defined, 🔄 In Development
**Last Review**: 2025-12-08

113
pyproject.toml Normal file
View File

@@ -0,0 +1,113 @@
[project]
name = "geutebruck-api"
version = "1.0.0"
description = "REST API for Geutebruck GeViScope/GeViSoft Cross-Switching Control"
authors = [
{name = "COLSYS", email = "info@colsys.tech"}
]
requires-python = ">=3.11"
readme = "README.md"
license = {text = "Proprietary"}
[project.urls]
Homepage = "https://git.colsys.tech/COLSYS/geutebruck-api"
Repository = "https://git.colsys.tech/COLSYS/geutebruck-api"
[tool.black]
line-length = 100
target-version = ['py311']
include = '\.pyi?$'
extend-exclude = '''
/(
# directories
\.eggs
| \.git
| \.hg
| \.mypy_cache
| \.tox
| \.venv
| build
| dist
| migrations
)/
'''
[tool.ruff]
line-length = 100
target-version = "py311"
[tool.ruff.lint]
select = [
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # pyflakes
"I", # isort
"C", # flake8-comprehensions
"B", # flake8-bugbear
"UP", # pyupgrade
]
ignore = [
"E501", # line too long (handled by black)
"B008", # do not perform function calls in argument defaults
"C901", # too complex
"W191", # indentation contains tabs
]
[tool.ruff.lint.per-file-ignores]
"__init__.py" = ["F401"] # unused imports in __init__.py
[tool.ruff.lint.isort]
known-third-party = ["fastapi", "pydantic", "sqlalchemy"]
[tool.mypy]
python_version = "3.11"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = false
disallow_incomplete_defs = false
check_untyped_defs = true
no_implicit_optional = true
warn_redundant_casts = true
warn_unused_ignores = true
warn_no_return = true
strict_equality = true
ignore_missing_imports = true
[[tool.mypy.overrides]]
module = "tests.*"
ignore_errors = true
[tool.pytest.ini_options]
minversion = "7.0"
addopts = "-ra -q --strict-markers --cov=src/api --cov-report=html --cov-report=term-missing"
testpaths = [
"tests",
]
pythonpath = [
"src/api"
]
asyncio_mode = "auto"
[tool.coverage.run]
source = ["src/api"]
omit = [
"*/tests/*",
"*/migrations/*",
"*/__init__.py",
]
[tool.coverage.report]
precision = 2
exclude_lines = [
"pragma: no cover",
"def __repr__",
"if TYPE_CHECKING:",
"raise AssertionError",
"raise NotImplementedError",
"if __name__ == .__main__.:",
"@abstractmethod",
]
[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"

57
requirements.txt Normal file
View File

@@ -0,0 +1,57 @@
# Web Framework
fastapi==0.109.0
uvicorn[standard]==0.27.0
python-multipart==0.0.6
# Database
sqlalchemy==2.0.25
alembic==1.13.1
psycopg2-binary==2.9.9
# Redis
redis==5.0.1
aioredis==2.0.1
# gRPC
grpcio==1.60.0
grpcio-tools==1.60.0
protobuf==4.25.2
# Authentication
pyjwt==2.8.0
passlib[bcrypt]==1.7.4
python-jose[cryptography]==3.3.0
# Validation
pydantic==2.5.3
pydantic-settings==2.1.0
email-validator==2.1.0
# WebSocket
websockets==12.0
# HTTP Client
httpx==0.26.0
aiohttp==3.9.1
# Testing
pytest==7.4.4
pytest-asyncio==0.23.3
pytest-cov==4.1.0
pytest-mock==3.12.0
httpx==0.26.0
# Code Quality
ruff==0.1.14
black==23.12.1
mypy==1.8.0
types-redis==4.6.0.20240106
# Environment
python-dotenv==1.0.0
# Logging
structlog==24.1.0
# Date/Time
python-dateutil==2.8.2

View File

@@ -0,0 +1,156 @@
# Geutebruck API - Development Environment Setup Script
# This script sets up the complete development environment
param(
[switch]$SkipPython,
[switch]$SkipDotnet,
[switch]$SkipDatabase,
[switch]$SkipRedis
)
$ErrorActionPreference = "Stop"
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "Geutebruck API - Development Setup" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host ""
$RepoRoot = Split-Path -Parent $PSScriptRoot
# Function to check if command exists
function Test-Command {
param($Command)
$null = Get-Command $Command -ErrorAction SilentlyContinue
return $?
}
# Check Prerequisites
Write-Host "[1/8] Checking prerequisites..." -ForegroundColor Yellow
if (-not $SkipPython) {
if (-not (Test-Command python)) {
Write-Host "ERROR: Python 3.11+ is required but not found" -ForegroundColor Red
Write-Host "Please install Python from https://www.python.org/downloads/" -ForegroundColor Red
exit 1
}
$pythonVersion = python --version
Write-Host " ✓ Python found: $pythonVersion" -ForegroundColor Green
}
if (-not $SkipDotnet) {
if (-not (Test-Command dotnet)) {
Write-Host "ERROR: .NET 8.0 SDK is required but not found" -ForegroundColor Red
Write-Host "Please install from https://dotnet.microsoft.com/download" -ForegroundColor Red
exit 1
}
$dotnetVersion = dotnet --version
Write-Host " ✓ .NET SDK found: $dotnetVersion" -ForegroundColor Green
}
# Create .env file if it doesn't exist
Write-Host "[2/8] Setting up environment configuration..." -ForegroundColor Yellow
if (-not (Test-Path "$RepoRoot\.env")) {
Copy-Item "$RepoRoot\.env.example" "$RepoRoot\.env"
Write-Host " ✓ Created .env file from .env.example" -ForegroundColor Green
Write-Host " ⚠ IMPORTANT: Edit .env to configure your settings!" -ForegroundColor Yellow
} else {
Write-Host " ✓ .env file already exists" -ForegroundColor Green
}
# Setup Python virtual environment
if (-not $SkipPython) {
Write-Host "[3/8] Setting up Python virtual environment..." -ForegroundColor Yellow
if (-not (Test-Path "$RepoRoot\.venv")) {
python -m venv "$RepoRoot\.venv"
Write-Host " ✓ Created Python virtual environment" -ForegroundColor Green
} else {
Write-Host " ✓ Virtual environment already exists" -ForegroundColor Green
}
# Activate virtual environment
& "$RepoRoot\.venv\Scripts\Activate.ps1"
# Upgrade pip
python -m pip install --upgrade pip | Out-Null
# Install Python dependencies
Write-Host "[4/8] Installing Python dependencies..." -ForegroundColor Yellow
pip install -r "$RepoRoot\requirements.txt"
Write-Host " ✓ Python dependencies installed" -ForegroundColor Green
} else {
Write-Host "[3/8] Skipping Python setup" -ForegroundColor Gray
Write-Host "[4/8] Skipping Python dependencies" -ForegroundColor Gray
}
# Build SDK Bridge
if (-not $SkipDotnet) {
Write-Host "[5/8] Building SDK Bridge (.NET gRPC service)..." -ForegroundColor Yellow
$sdkBridgePath = "$RepoRoot\src\sdk-bridge\GeViScopeBridge"
if (Test-Path "$sdkBridgePath\GeViScopeBridge.csproj") {
Push-Location $sdkBridgePath
dotnet restore
dotnet build --configuration Debug
Pop-Location
Write-Host " ✓ SDK Bridge built successfully" -ForegroundColor Green
} else {
Write-Host " ⚠ SDK Bridge project not found, skipping" -ForegroundColor Yellow
}
} else {
Write-Host "[5/8] Skipping .NET build" -ForegroundColor Gray
}
# Setup PostgreSQL Database
if (-not $SkipDatabase) {
Write-Host "[6/8] Setting up PostgreSQL database..." -ForegroundColor Yellow
if (Test-Command psql) {
# Create database
Write-Host " Creating database 'geutebruck_api'..." -ForegroundColor Cyan
$createDbCommand = @"
CREATE DATABASE geutebruck_api;
CREATE USER geutebruck WITH PASSWORD 'geutebruck';
GRANT ALL PRIVILEGES ON DATABASE geutebruck_api TO geutebruck;
"@
Write-Host " Run these commands manually in psql:" -ForegroundColor Yellow
Write-Host $createDbCommand -ForegroundColor White
Write-Host ""
Write-Host " Then run: alembic upgrade head" -ForegroundColor Yellow
} else {
Write-Host " ⚠ PostgreSQL not found. Install PostgreSQL 14+ manually" -ForegroundColor Yellow
Write-Host " Download from: https://www.postgresql.org/download/windows/" -ForegroundColor Yellow
}
} else {
Write-Host "[6/8] Skipping database setup" -ForegroundColor Gray
}
# Check Redis
if (-not $SkipRedis) {
Write-Host "[7/8] Checking Redis..." -ForegroundColor Yellow
if (Test-Command redis-server) {
Write-Host " ✓ Redis found" -ForegroundColor Green
} else {
Write-Host " ⚠ Redis not found. Install Redis for Windows:" -ForegroundColor Yellow
Write-Host " Option 1: choco install redis-64" -ForegroundColor Yellow
Write-Host " Option 2: Download from https://redis.io/download" -ForegroundColor Yellow
}
} else {
Write-Host "[7/8] Skipping Redis check" -ForegroundColor Gray
}
# Summary
Write-Host "[8/8] Setup complete!" -ForegroundColor Yellow
Write-Host ""
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "Next Steps:" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "1. Edit .env file with your GeViServer credentials" -ForegroundColor White
Write-Host "2. Ensure PostgreSQL is running and database is created" -ForegroundColor White
Write-Host "3. Run database migrations: alembic upgrade head" -ForegroundColor White
Write-Host "4. Ensure Redis is running: redis-server" -ForegroundColor White
Write-Host "5. Start services: .\scripts\start_services.ps1" -ForegroundColor White
Write-Host ""
Write-Host "Development Environment Ready! 🚀" -ForegroundColor Green

114
scripts/start_services.ps1 Normal file
View File

@@ -0,0 +1,114 @@
# Geutebruck API - Start All Services
# This script starts Redis, SDK Bridge, and FastAPI in separate windows
param(
[switch]$SkipRedis,
[switch]$SkipSdkBridge,
[switch]$SkipApi
)
$ErrorActionPreference = "Stop"
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "Geutebruck API - Starting Services" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host ""
$RepoRoot = Split-Path -Parent $PSScriptRoot
# Check if .env exists
if (-not (Test-Path "$RepoRoot\.env")) {
Write-Host "ERROR: .env file not found!" -ForegroundColor Red
Write-Host "Run: .\scripts\setup_dev_environment.ps1 first" -ForegroundColor Red
exit 1
}
# Function to check if port is in use
function Test-Port {
param([int]$Port)
$tcpConnection = Get-NetTCPConnection -LocalPort $Port -ErrorAction SilentlyContinue
return $null -ne $tcpConnection
}
# Start Redis
if (-not $SkipRedis) {
Write-Host "[1/3] Starting Redis..." -ForegroundColor Yellow
if (Test-Port 6379) {
Write-Host " ✓ Redis already running on port 6379" -ForegroundColor Green
} else {
$redisCmd = Get-Command redis-server -ErrorAction SilentlyContinue
if ($redisCmd) {
Start-Process -FilePath "redis-server" -WindowStyle Normal
Start-Sleep -Seconds 2
Write-Host " ✓ Redis started" -ForegroundColor Green
} else {
Write-Host " ✗ Redis not found. Install with: choco install redis-64" -ForegroundColor Red
}
}
} else {
Write-Host "[1/3] Skipping Redis" -ForegroundColor Gray
}
# Start SDK Bridge
if (-not $SkipSdkBridge) {
Write-Host "[2/3] Starting SDK Bridge (gRPC Service)..." -ForegroundColor Yellow
$sdkBridgePath = "$RepoRoot\src\sdk-bridge\GeViScopeBridge"
$sdkBridgeExe = "$sdkBridgePath\bin\Debug\net8.0\GeViScopeBridge.exe"
if (Test-Path $sdkBridgeExe) {
if (Test-Port 50051) {
Write-Host " ✓ SDK Bridge already running on port 50051" -ForegroundColor Green
} else {
$sdkBridgeTitle = "Geutebruck SDK Bridge"
Start-Process powershell -ArgumentList "-NoExit", "-Command", "cd '$sdkBridgePath'; dotnet run --configuration Debug" -WindowStyle Normal
Start-Sleep -Seconds 3
Write-Host " ✓ SDK Bridge started on port 50051" -ForegroundColor Green
}
} else {
Write-Host " ⚠ SDK Bridge not built yet" -ForegroundColor Yellow
Write-Host " Run: cd $sdkBridgePath; dotnet build" -ForegroundColor Yellow
}
} else {
Write-Host "[2/3] Skipping SDK Bridge" -ForegroundColor Gray
}
# Start FastAPI
if (-not $SkipApi) {
Write-Host "[3/3] Starting FastAPI Application..." -ForegroundColor Yellow
$apiPath = "$RepoRoot\src\api"
if (Test-Port 8000) {
Write-Host " ✓ API already running on port 8000" -ForegroundColor Green
} else {
# Check if virtual environment exists
if (Test-Path "$RepoRoot\.venv\Scripts\Activate.ps1") {
$apiTitle = "Geutebruck API"
$startCommand = "cd '$apiPath'; & '$RepoRoot\.venv\Scripts\Activate.ps1'; uvicorn main:app --reload --host 0.0.0.0 --port 8000"
Start-Process powershell -ArgumentList "-NoExit", "-Command", $startCommand -WindowStyle Normal
Start-Sleep -Seconds 3
Write-Host " ✓ FastAPI started on http://localhost:8000" -ForegroundColor Green
} else {
Write-Host " ✗ Python virtual environment not found" -ForegroundColor Red
Write-Host " Run: .\scripts\setup_dev_environment.ps1 first" -ForegroundColor Red
}
}
} else {
Write-Host "[3/3] Skipping FastAPI" -ForegroundColor Gray
}
# Summary
Write-Host ""
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "Services Status:" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "Redis: http://localhost:6379" -ForegroundColor White
Write-Host "SDK Bridge: http://localhost:50051 (gRPC)" -ForegroundColor White
Write-Host "API: http://localhost:8000" -ForegroundColor White
Write-Host "API Docs: http://localhost:8000/docs" -ForegroundColor White
Write-Host ""
Write-Host "All Services Started! 🚀" -ForegroundColor Green
Write-Host ""
Write-Host "Press Ctrl+C in each window to stop services" -ForegroundColor Yellow

View File

@@ -0,0 +1,411 @@
# Tasks: Geutebruck Cross-Switching API (Revised MVP)
**Scope**: Cross-switching REST API with authentication, focusing on GeViSet-compatible configuration
**MVP Goal**: Control GSCView viewers via cross-switching, no UI needed
**Future Expansion**: GeViSet configuration management, action mapping, CSV import/export
---
## MVP User Stories
### US1: Authentication & Connection
Connect to GeViServer, authenticate users, maintain sessions
### US2: Camera Discovery
List all video inputs (cameras) with metadata
### US3: Monitor Discovery
List all video outputs (GSCView viewers/monitors) with status
### US4: Cross-Switching Operations
Route cameras to viewers, clear viewers, query routing state
---
## Revised Data Model (Simplified)
```
User:
- id, username, password_hash, role (viewer/operator/admin)
Camera:
- id (channel), name, description, has_ptz, has_video_sensor, status
Monitor:
- id (output channel), name, is_active, current_camera_id
CrossSwitchRoute:
- id, camera_id, monitor_id, switched_at, switched_by_user_id
AuditLog:
- id, user_id, action, target, timestamp, details
```
---
## Phase 1: Foundation (Setup & Core Infrastructure)
**Purpose**: Project structure, dependencies, SDK bridge foundation
- [ ] T001 Create project structure (src/api, src/sdk-bridge, tests, docs, scripts)
- [ ] T002 Create .gitignore for Python and C#
- [ ] T003 Create requirements.txt with FastAPI, SQLAlchemy, Redis, grpcio, PyJWT, pytest
- [ ] T004 Create SDK Bridge .csproj with .NET 8.0, Grpc.AspNetCore, GeViScope SDK reference
- [ ] T005 Create .env.example with config variables (DB, Redis, JWT secret, GeViServer host/credentials)
- [ ] T006 Create alembic.ini for database migrations
- [ ] T007 [P] Create pyproject.toml with ruff, black, mypy configuration
- [ ] T008 [P] Create scripts/setup_dev_environment.ps1 (install dependencies, setup DB, start services)
- [ ] T009 [P] Create scripts/start_services.ps1 (start Redis, SDK Bridge, FastAPI)
- [ ] T010 [P] Create docs/architecture.md documenting system design
**Checkpoint**: Project structure complete, dependencies defined
---
## Phase 2: SDK Bridge Foundation (C# gRPC Service)
**Purpose**: Wrap GeViScope SDK with gRPC for Python consumption
### gRPC Protocol Definitions
- [ ] T011 Define common.proto (Status, Error, Timestamp, Empty messages)
- [ ] T012 Define camera.proto (ListCamerasRequest/Response, CameraInfo with channel, name, has_ptz)
- [ ] T013 Define monitor.proto (ListMonitorsRequest/Response, MonitorInfo with channel, name, current_camera)
- [ ] T014 Define crossswitch.proto (CrossSwitchRequest, ClearMonitorRequest, GetRoutingStateRequest/Response)
### SDK Wrapper Implementation
- [ ] T015 Create GeViDatabaseWrapper.cs (Create, RegisterCallback, Connect, Disconnect, error handling)
- [ ] T016 Implement connection lifecycle with retry logic (3 attempts, exponential backoff)
- [ ] T017 Create StateQueryHandler.cs for GetFirst/GetNext enumeration pattern
- [ ] T018 Implement EnumerateCameras() using CSQGetFirstVideoInput / CSQGetNextVideoInput
- [ ] T019 Implement EnumerateMonitors() using CSQGetFirstVideoOutput / CSQGetNextVideoOutput
- [ ] T020 Create ErrorTranslator.cs to map Windows error codes to gRPC status codes
- [ ] T021 Create ActionDispatcher.cs for sending SDK actions (CrossSwitch, ClearVideoOutput)
### gRPC Service Implementation
- [ ] T022 Create CameraService.cs implementing camera.proto with ListCameras RPC
- [ ] T023 Create MonitorService.cs implementing monitor.proto with ListMonitors RPC
- [ ] T024 Create CrossSwitchService.cs with ExecuteCrossSwitch, ClearMonitor, GetRoutingState RPCs
- [ ] T025 Create Program.cs gRPC server with Serilog logging, service registration
- [ ] T026 Add configuration loading from appsettings.json (GeViServer host, port, credentials)
**Checkpoint**: SDK Bridge can connect to GeViServer, enumerate resources, execute cross-switch
---
## Phase 3: Python API Foundation
**Purpose**: FastAPI application structure, configuration, database setup
### Core Setup
- [ ] T027 Create main.py with FastAPI app, CORS middleware, exception handlers
- [ ] T028 Create config.py loading settings from environment (Pydantic BaseSettings)
- [ ] T029 Setup PostgreSQL connection with SQLAlchemy async engine in models/__init__.py
- [ ] T030 Create initial Alembic migration for users and audit_logs tables
- [ ] T031 Setup Redis client with connection pooling in clients/redis_client.py
- [ ] T032 Create gRPC SDK Bridge client in clients/sdk_bridge_client.py with connection pooling
- [ ] T033 [P] Create JWT utilities in utils/jwt_utils.py (encode, decode, verify)
- [ ] T034 [P] Create error translation utilities in utils/error_translation.py (gRPC → HTTP status)
- [ ] T035 Implement global error handler middleware in middleware/error_handler.py
### Database Models
- [ ] T036 [P] Create User model in models/user.py (id, username, password_hash, role, created_at)
- [ ] T037 [P] Create AuditLog model in models/audit_log.py (id, user_id, action, target, timestamp)
- [ ] T038 Run alembic upgrade head to create tables
**Checkpoint**: Python API can start, connect to DB/Redis, communicate with SDK Bridge via gRPC
---
## Phase 4: Authentication (User Story 1)
**Purpose**: JWT-based authentication with role-based access control
### Tests (TDD - Write FIRST, Ensure FAIL)
- [ ] T039 [P] Write contract test for POST /api/v1/auth/login in tests/api/contract/test_auth.py (should FAIL)
- [ ] T040 [P] Write contract test for POST /api/v1/auth/logout in tests/api/contract/test_auth.py (should FAIL)
- [ ] T041 [P] Write unit test for AuthService in tests/api/unit/test_auth_service.py (should FAIL)
### Implementation
- [ ] T042 [P] Create auth schemas in schemas/auth.py (LoginRequest, TokenResponse, UserInfo)
- [ ] T043 Implement AuthService in services/auth_service.py (login, logout, validate_token, hash_password)
- [ ] T044 Implement JWT token generation (access: 1hr, refresh: 7 days) with Redis session storage
- [ ] T045 Implement authentication middleware in middleware/auth_middleware.py (verify JWT, extract user)
- [ ] T046 Implement role checking decorator in utils/permissions.py (@require_role("operator"))
- [ ] T047 Create auth router in routers/auth.py with POST /auth/login, POST /auth/logout
- [ ] T048 Add audit logging for authentication attempts (success and failures)
**Verify**: Run tests T039-T041 - should now PASS
**Checkpoint**: Can login with credentials, receive JWT token, use token for authenticated requests
---
## Phase 5: Camera Discovery (User Story 2)
**Purpose**: List all cameras (video inputs) from GeViServer
### Tests (TDD - Write FIRST, Ensure FAIL)
- [ ] T049 [P] Write contract test for GET /api/v1/cameras in tests/api/contract/test_cameras.py (should FAIL)
- [ ] T050 [P] Write unit test for CameraService in tests/api/unit/test_camera_service.py (should FAIL)
### Implementation
- [ ] T051 [P] Create camera schemas in schemas/camera.py (CameraInfo, CameraList)
- [ ] T052 Implement CameraService in services/camera_service.py (list_cameras via gRPC to SDK Bridge)
- [ ] T053 Create cameras router in routers/cameras.py with GET /cameras
- [ ] T054 Add permission check: authenticated users only
- [ ] T055 Add caching in Redis (cache camera list for 60 seconds to reduce SDK Bridge load)
**Verify**: Run tests T049-T050 - should now PASS
**Checkpoint**: GET /api/v1/cameras returns list of all cameras from GeViServer
---
## Phase 6: Monitor Discovery (User Story 3)
**Purpose**: List all monitors/viewers (video outputs) from GeViServer
### Tests (TDD - Write FIRST, Ensure FAIL)
- [ ] T056 [P] Write contract test for GET /api/v1/monitors in tests/api/contract/test_monitors.py (should FAIL)
- [ ] T057 [P] Write unit test for MonitorService in tests/api/unit/test_monitor_service.py (should FAIL)
### Implementation
- [ ] T058 [P] Create monitor schemas in schemas/monitor.py (MonitorInfo, MonitorList)
- [ ] T059 Implement MonitorService in services/monitor_service.py (list_monitors via gRPC to SDK Bridge)
- [ ] T060 Create monitors router in routers/monitors.py with GET /monitors
- [ ] T061 Add permission check: authenticated users only
- [ ] T062 Add caching in Redis (cache monitor list for 60 seconds)
**Verify**: Run tests T056-T057 - should now PASS
**Checkpoint**: GET /api/v1/monitors returns list of all monitors/viewers from GeViServer
---
## Phase 7: Cross-Switching Operations (User Story 4)
**Purpose**: Execute cross-switch, clear monitors, query routing state
### Tests (TDD - Write FIRST, Ensure FAIL)
- [ ] T063 [P] Write contract test for POST /api/v1/crossswitch in tests/api/contract/test_crossswitch.py (should FAIL)
- [ ] T064 [P] Write contract test for DELETE /api/v1/monitors/{id} in tests/api/contract/test_crossswitch.py (should FAIL)
- [ ] T065 [P] Write contract test for GET /api/v1/routing/state in tests/api/contract/test_crossswitch.py (should FAIL)
- [ ] T066 [P] Write integration test for cross-switch workflow in tests/api/integration/test_crossswitch.py (should FAIL)
### Implementation
- [ ] T067 [P] Create crossswitch schemas in schemas/crossswitch.py (CrossSwitchRequest, RoutingState, ClearMonitorRequest)
- [ ] T068 Create CrossSwitchRoute model in models/crossswitch_route.py (id, camera_id, monitor_id, switched_at, user_id)
- [ ] T069 Create Alembic migration for crossswitch_routes table
- [ ] T070 Implement CrossSwitchService in services/crossswitch_service.py:
- execute_crossswitch(camera_id, monitor_id, mode=0) → gRPC to SDK Bridge
- clear_monitor(monitor_id) → gRPC ClearVideoOutput
- get_routing_state() → query current routes
- [ ] T071 Create crossswitch router in routers/crossswitch.py:
- POST /crossswitch (requires operator or admin role)
- DELETE /monitors/{id} (requires operator or admin role)
- GET /routing/state (all authenticated users)
- [ ] T072 Add audit logging for all cross-switch operations
- [ ] T073 Add validation: camera_id and monitor_id must exist
- [ ] T074 Store routing state in database for history/tracking
**Verify**: Run tests T063-T066 - should now PASS
**Checkpoint**: Can execute cross-switch via API, clear monitors, query current routing
---
## Phase 8: MVP Polish & Documentation
**Purpose**: Complete MVP with documentation and deployment readiness
- [ ] T075 [P] Create API documentation in docs/api-usage.md with curl examples
- [ ] T076 [P] Create deployment guide in docs/deployment.md (Windows Server setup, service installation)
- [ ] T077 [P] Add Prometheus metrics endpoint at /metrics (request count, latency, active connections)
- [ ] T078 [P] Create health check endpoint GET /health (SDK Bridge connectivity, DB, Redis status)
- [ ] T079 [P] Add request logging with correlation IDs
- [ ] T080 Create README.md with project overview, quick start, architecture diagram
- [ ] T081 Update OpenAPI specification to include only MVP endpoints
- [ ] T082 Create Postman collection for API testing
- [ ] T083 Run full integration tests with actual GeViServer connection
- [ ] T084 Security audit: Remove stack traces in production, sanitize logs
**Checkpoint**: MVP complete - REST API for cross-switching with authentication
---
## Phase 9: Future - GeViSet Configuration Management (Phase 2)
**Purpose**: GeViSet-like functionality via API (action mapping configuration)
**Note**: These tasks will be detailed after MVP is complete and working
### High-Level Tasks:
- [ ] T085 Research GeViSet configuration file format and action mapping structure
- [ ] T086 Implement GET /api/v1/config/actions to retrieve action mappings from GeViServer
- [ ] T087 Implement PUT /api/v1/config/actions to push action mappings to GeViServer
- [ ] T088 Implement POST /api/v1/config/actions/export to export configuration to CSV
- [ ] T089 Implement POST /api/v1/config/actions/import to import configuration from CSV
- [ ] T090 Add validation for action mapping syntax and constraints
- [ ] T091 Add versioning for configuration changes (track who changed what, when)
- [ ] T092 Add backup/restore functionality for configurations
**Checkpoint**: GeViSet configuration management available via API
---
## Dependencies & Execution Order
### Phase Dependencies
```
Phase 1 (Setup)
Phase 2 (SDK Bridge Foundation) ← BLOCKS all Python API work
Phase 3 (Python API Foundation) ← BLOCKS all feature work
Phase 4 (Authentication) ← BLOCKS all protected endpoints
Phases 5, 6, 7 can proceed in parallel (after Phase 4)
Phase 8 (Polish & Documentation)
Phase 9 (Future - GeViSet config) ← After MVP validated
```
### Critical Path (Sequential)
1. Setup → SDK Bridge → Python API → Authentication
2. Then parallel: Camera Discovery + Monitor Discovery + Cross-Switching
3. Then: Polish & Documentation
4. Finally: GeViSet configuration (Phase 2)
### Parallel Opportunities
- Phase 2: T020 (ErrorTranslator) parallel with T017-T019 (StateQuery implementation)
- Phase 3: T033-T034, T036-T037 can run in parallel
- Phase 4: T039-T041 tests can run in parallel
- Phase 5-7: These entire phases can run in parallel after Phase 4 completes
- Phase 8: T075-T082 can run in parallel
---
## Implementation Strategy
### Week 1: Foundation
- Days 1-2: Phase 1 (Setup)
- Days 3-5: Phase 2 (SDK Bridge)
### Week 2: API Core
- Days 1-3: Phase 3 (Python API Foundation)
- Days 4-5: Phase 4 (Authentication)
### Week 3: Cross-Switching
- Days 1-2: Phase 5 (Camera Discovery)
- Days 2-3: Phase 6 (Monitor Discovery)
- Days 4-5: Phase 7 (Cross-Switching Operations)
### Week 4: Polish & Validation
- Days 1-3: Phase 8 (Polish, Documentation)
- Days 4-5: Integration testing with real GeViServer, bug fixes
**MVP Delivery**: End of Week 4
### Week 5+: Phase 2 Features
- GeViSet configuration management
- Action mapping CRUD
- CSV import/export
---
## Task Summary
**MVP Total**: 84 tasks
**By Phase**:
- Phase 1 (Setup): 10 tasks
- Phase 2 (SDK Bridge): 16 tasks
- Phase 3 (API Foundation): 12 tasks
- Phase 4 (Authentication): 10 tasks
- Phase 5 (Camera Discovery): 7 tasks
- Phase 6 (Monitor Discovery): 7 tasks
- Phase 7 (Cross-Switching): 12 tasks
- Phase 8 (Polish): 10 tasks
**Phase 2 (Future)**: 8+ tasks (detailed after MVP)
**Tests**: 12 test tasks (TDD approach)
**Parallel Tasks**: 20+ tasks marked [P]
**Estimated Timeline**:
- MVP: 3-4 weeks (1 developer, focused work)
- Phase 2 (GeViSet config): +1-2 weeks
---
## MVP Endpoints Summary
```
# Authentication
POST /api/v1/auth/login # Get JWT token
POST /api/v1/auth/logout # Invalidate token
# Cameras
GET /api/v1/cameras # List all cameras
# Monitors
GET /api/v1/monitors # List all monitors/viewers
# Cross-Switching
POST /api/v1/crossswitch # Execute cross-switch
Body: { camera_id: 7, monitor_id: 3, mode: 0 }
DELETE /api/v1/monitors/{id} # Clear monitor (stop video)
GET /api/v1/routing/state # Get current routing state
# System
GET /api/v1/health # Health check (SDK Bridge, DB, Redis)
GET /metrics # Prometheus metrics
```
---
## Testing Strategy
### TDD Approach
1. Write contract test (should FAIL)
2. Write unit tests (should FAIL)
3. Implement feature
4. Run tests (should PASS)
5. Refactor if needed
6. Commit
### Test Coverage Goal
- Minimum 70% coverage for MVP
- 100% coverage for authentication and cross-switching logic
### Manual Testing
- Test with Postman collection
- Test with curl commands
- Integration test with actual GeViServer
---
**Generated**: 2025-12-08
**Scope**: Cross-switching MVP with authentication, expandable to GeViSet configuration
**Architecture**: Python FastAPI + C# gRPC Bridge + GeViScope SDK

View File

@@ -0,0 +1,34 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<PlatformTarget>x86</PlatformTarget>
<Platforms>x86</Platforms>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Grpc.AspNetCore" Version="2.60.0" />
<PackageReference Include="Grpc.Tools" Version="2.60.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Google.Protobuf" Version="3.25.2" />
<PackageReference Include="Serilog" Version="3.1.1" />
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.1" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
</ItemGroup>
<ItemGroup>
<Protobuf Include="..\Protos\*.proto" GrpcServices="Server" />
</ItemGroup>
<ItemGroup>
<Reference Include="GeViProcAPINET_4_0">
<HintPath>C:\GEVISOFT\GeViProcAPINET_4_0.dll</HintPath>
</Reference>
</ItemGroup>
</Project>