diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..bac490c --- /dev/null +++ b/.env.example @@ -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 diff --git a/alembic.ini b/alembic.ini new file mode 100644 index 0000000..7cf3403 --- /dev/null +++ b/alembic.ini @@ -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 diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..d759ebd --- /dev/null +++ b/docs/architecture.md @@ -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 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..420b489 --- /dev/null +++ b/pyproject.toml @@ -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" diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..4685331 --- /dev/null +++ b/requirements.txt @@ -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 diff --git a/scripts/setup_dev_environment.ps1 b/scripts/setup_dev_environment.ps1 new file mode 100644 index 0000000..d17eb25 --- /dev/null +++ b/scripts/setup_dev_environment.ps1 @@ -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 diff --git a/scripts/start_services.ps1 b/scripts/start_services.ps1 new file mode 100644 index 0000000..57ac530 --- /dev/null +++ b/scripts/start_services.ps1 @@ -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 diff --git a/specs/001-surveillance-api/tasks-revised-mvp.md b/specs/001-surveillance-api/tasks-revised-mvp.md new file mode 100644 index 0000000..d8045eb --- /dev/null +++ b/specs/001-surveillance-api/tasks-revised-mvp.md @@ -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 diff --git a/src/sdk-bridge/GeViScopeBridge/GeViScopeBridge.csproj b/src/sdk-bridge/GeViScopeBridge/GeViScopeBridge.csproj new file mode 100644 index 0000000..2ec52d1 --- /dev/null +++ b/src/sdk-bridge/GeViScopeBridge/GeViScopeBridge.csproj @@ -0,0 +1,34 @@ + + + + Exe + net8.0 + enable + enable + x86 + x86 + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + + + + C:\GEVISOFT\GeViProcAPINET_4_0.dll + + + +