- Add comprehensive research.md with SDK integration decisions - Add complete data-model.md with 7 entities and relationships - Add OpenAPI 3.0 specification (contracts/openapi.yaml) - Add developer quickstart.md guide - Add comprehensive tasks.md with 215 tasks organized by user story - Update plan.md with complete technical context - Add SDK_INTEGRATION_LESSONS.md capturing critical knowledge - Add .gitignore for Python and C# projects - Include GeViScopeConfigReader and GeViSoftConfigReader tools Phase 1 Design Complete: ✅ Architecture: Python FastAPI + C# gRPC Bridge + GeViScope SDK ✅ 10 user stories mapped to tasks (MVP = US1-4) ✅ Complete API contract with 17 endpoints ✅ Data model with User, Camera, Stream, Event, Recording, Analytics ✅ TDD approach enforced with 80+ test tasks Ready for Phase 2: Implementation 🤖 Generated with Claude Code (https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1397 lines
38 KiB
YAML
1397 lines
38 KiB
YAML
openapi: 3.0.3
|
|
info:
|
|
title: Geutebruck Video Surveillance API
|
|
description: |
|
|
Complete RESTful API for Geutebruck GeViScope/GeViSoft video surveillance system control.
|
|
|
|
**Features:**
|
|
- JWT-based authentication with refresh tokens
|
|
- Live video streaming from surveillance cameras
|
|
- PTZ (Pan-Tilt-Zoom) camera control
|
|
- Real-time event notifications via WebSocket
|
|
- Video recording management
|
|
- Video analytics configuration (VMD, NPR, OBTRACK)
|
|
- Multi-camera management
|
|
- System health monitoring
|
|
|
|
**Authentication:**
|
|
All endpoints except `/health` and `/docs` require Bearer token authentication.
|
|
Obtain tokens via `/api/v1/auth/login` endpoint.
|
|
|
|
**Rate Limiting:**
|
|
- Authentication endpoints: 5 requests/minute per IP
|
|
- All other endpoints: 500 requests/minute per user
|
|
|
|
**Support:**
|
|
- Documentation: https://docs.example.com
|
|
- GitHub: https://github.com/example/geutebruck-api
|
|
version: 1.0.0
|
|
contact:
|
|
name: API Support
|
|
email: api-support@example.com
|
|
license:
|
|
name: MIT
|
|
url: https://opensource.org/licenses/MIT
|
|
|
|
servers:
|
|
- url: https://api.example.com
|
|
description: Production server
|
|
- url: https://staging-api.example.com
|
|
description: Staging server
|
|
- url: http://localhost:8000
|
|
description: Development server
|
|
|
|
tags:
|
|
- name: Authentication
|
|
description: User authentication and session management
|
|
- name: Cameras
|
|
description: Camera management and live streaming
|
|
- name: Events
|
|
description: Real-time event subscriptions and history
|
|
- name: Recordings
|
|
description: Video recording management
|
|
- name: Analytics
|
|
description: Video analytics configuration
|
|
- name: System
|
|
description: System health and status
|
|
|
|
paths:
|
|
# ============================================================================
|
|
# AUTHENTICATION ENDPOINTS
|
|
# ============================================================================
|
|
|
|
/api/v1/auth/login:
|
|
post:
|
|
tags: [Authentication]
|
|
summary: Authenticate user and obtain JWT tokens
|
|
description: |
|
|
Validates user credentials and returns access + refresh JWT tokens.
|
|
Access tokens expire in 1 hour, refresh tokens in 7 days.
|
|
operationId: login
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/LoginRequest'
|
|
examples:
|
|
operator_login:
|
|
summary: Operator login
|
|
value:
|
|
username: operator1
|
|
password: SecurePass123!
|
|
responses:
|
|
'200':
|
|
description: Authentication successful
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/TokenPair'
|
|
examples:
|
|
success:
|
|
value:
|
|
access_token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
|
|
refresh_token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
|
|
token_type: bearer
|
|
expires_in: 3600
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
'429':
|
|
$ref: '#/components/responses/TooManyRequests'
|
|
|
|
/api/v1/auth/refresh:
|
|
post:
|
|
tags: [Authentication]
|
|
summary: Refresh access token
|
|
description: Obtain a new access token using a valid refresh token
|
|
operationId: refreshToken
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/RefreshTokenRequest'
|
|
responses:
|
|
'200':
|
|
description: New access token issued
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/AccessToken'
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
|
|
/api/v1/auth/logout:
|
|
post:
|
|
tags: [Authentication]
|
|
summary: Logout and invalidate tokens
|
|
description: Invalidates current session and revokes all tokens
|
|
operationId: logout
|
|
security:
|
|
- BearerAuth: []
|
|
responses:
|
|
'204':
|
|
description: Logout successful
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
|
|
# ============================================================================
|
|
# CAMERA ENDPOINTS
|
|
# ============================================================================
|
|
|
|
/api/v1/cameras:
|
|
get:
|
|
tags: [Cameras]
|
|
summary: List all cameras
|
|
description: |
|
|
Returns all cameras the authenticated user has permission to view.
|
|
Results include camera status, capabilities, and current recording state.
|
|
operationId: listCameras
|
|
security:
|
|
- BearerAuth: []
|
|
parameters:
|
|
- name: status
|
|
in: query
|
|
description: Filter by camera status
|
|
schema:
|
|
$ref: '#/components/schemas/CameraStatus'
|
|
- name: location
|
|
in: query
|
|
description: Filter by location
|
|
schema:
|
|
type: string
|
|
- name: has_ptz
|
|
in: query
|
|
description: Filter cameras with PTZ capability
|
|
schema:
|
|
type: boolean
|
|
- name: limit
|
|
in: query
|
|
description: Maximum number of results
|
|
schema:
|
|
type: integer
|
|
default: 50
|
|
minimum: 1
|
|
maximum: 1000
|
|
- name: offset
|
|
in: query
|
|
description: Pagination offset
|
|
schema:
|
|
type: integer
|
|
default: 0
|
|
minimum: 0
|
|
responses:
|
|
'200':
|
|
description: List of cameras
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
cameras:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/Camera'
|
|
total:
|
|
type: integer
|
|
description: Total number of cameras (before pagination)
|
|
limit:
|
|
type: integer
|
|
offset:
|
|
type: integer
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
|
|
/api/v1/cameras/{camera_id}:
|
|
get:
|
|
tags: [Cameras]
|
|
summary: Get camera details
|
|
description: Returns detailed information about a specific camera
|
|
operationId: getCamera
|
|
security:
|
|
- BearerAuth: []
|
|
parameters:
|
|
- $ref: '#/components/parameters/CameraId'
|
|
responses:
|
|
'200':
|
|
description: Camera details
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Camera'
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
'403':
|
|
$ref: '#/components/responses/Forbidden'
|
|
'404':
|
|
$ref: '#/components/responses/NotFound'
|
|
|
|
/api/v1/cameras/{camera_id}/stream:
|
|
post:
|
|
tags: [Cameras]
|
|
summary: Request live video stream
|
|
description: |
|
|
Returns an authenticated URL for accessing the live video stream.
|
|
The URL includes a time-limited JWT token and expires in 1 hour.
|
|
Clients connect directly to the GeViScope stream URL (no proxy).
|
|
operationId: requestStream
|
|
security:
|
|
- BearerAuth: []
|
|
parameters:
|
|
- $ref: '#/components/parameters/CameraId'
|
|
requestBody:
|
|
required: false
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/StreamRequest'
|
|
examples:
|
|
default:
|
|
summary: Default stream (H.264, max resolution)
|
|
value:
|
|
format: h264
|
|
quality: 90
|
|
low_bandwidth:
|
|
summary: Low bandwidth stream
|
|
value:
|
|
format: mjpeg
|
|
resolution: "640x480"
|
|
fps: 15
|
|
quality: 60
|
|
responses:
|
|
'200':
|
|
description: Stream URL created
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/StreamResponse'
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
'403':
|
|
$ref: '#/components/responses/Forbidden'
|
|
'404':
|
|
$ref: '#/components/responses/NotFound'
|
|
'503':
|
|
description: Camera offline or unavailable
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Error'
|
|
|
|
/api/v1/cameras/{camera_id}/ptz:
|
|
post:
|
|
tags: [Cameras]
|
|
summary: Send PTZ control command
|
|
description: |
|
|
Controls pan, tilt, and zoom operations on PTZ-capable cameras.
|
|
Commands execute with <500ms latency from request to camera movement.
|
|
operationId: controlPTZ
|
|
security:
|
|
- BearerAuth: []
|
|
parameters:
|
|
- $ref: '#/components/parameters/CameraId'
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/PTZCommand'
|
|
examples:
|
|
pan_left:
|
|
summary: Pan camera left
|
|
value:
|
|
action: pan_left
|
|
speed: 50
|
|
goto_preset:
|
|
summary: Move to preset position
|
|
value:
|
|
action: goto_preset
|
|
preset_id: 3
|
|
responses:
|
|
'200':
|
|
description: PTZ command accepted
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
message:
|
|
type: string
|
|
example: PTZ command executed
|
|
camera_id:
|
|
type: integer
|
|
action:
|
|
type: string
|
|
'400':
|
|
description: Invalid command or camera lacks PTZ
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Error'
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
'403':
|
|
$ref: '#/components/responses/Forbidden'
|
|
'404':
|
|
$ref: '#/components/responses/NotFound'
|
|
|
|
/api/v1/cameras/{camera_id}/presets:
|
|
get:
|
|
tags: [Cameras]
|
|
summary: List PTZ presets
|
|
description: Returns all saved PTZ preset positions for this camera
|
|
operationId: listPresets
|
|
security:
|
|
- BearerAuth: []
|
|
parameters:
|
|
- $ref: '#/components/parameters/CameraId'
|
|
responses:
|
|
'200':
|
|
description: List of presets
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/PTZPreset'
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
'403':
|
|
$ref: '#/components/responses/Forbidden'
|
|
'404':
|
|
$ref: '#/components/responses/NotFound'
|
|
|
|
post:
|
|
tags: [Cameras]
|
|
summary: Save PTZ preset
|
|
description: Saves current camera position as a named preset
|
|
operationId: savePreset
|
|
security:
|
|
- BearerAuth: []
|
|
parameters:
|
|
- $ref: '#/components/parameters/CameraId'
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required:
|
|
- name
|
|
properties:
|
|
name:
|
|
type: string
|
|
minLength: 1
|
|
maxLength: 50
|
|
example: "Main Entrance View"
|
|
responses:
|
|
'201':
|
|
description: Preset created
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/PTZPreset'
|
|
'400':
|
|
$ref: '#/components/responses/BadRequest'
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
'403':
|
|
$ref: '#/components/responses/Forbidden'
|
|
|
|
# ============================================================================
|
|
# EVENT ENDPOINTS
|
|
# ============================================================================
|
|
|
|
/api/v1/events/stream:
|
|
get:
|
|
tags: [Events]
|
|
summary: WebSocket event stream
|
|
description: |
|
|
**WebSocket endpoint** for real-time event notifications.
|
|
|
|
**Connection Flow:**
|
|
1. Client upgrades HTTP to WebSocket connection
|
|
2. Client sends subscription message with filters
|
|
3. Server sends matching events as they occur
|
|
4. Client sends heartbeat (ping) every 30s
|
|
5. Server responds with pong
|
|
|
|
**Message Format:**
|
|
```json
|
|
// Subscribe
|
|
{
|
|
"action": "subscribe",
|
|
"filters": {
|
|
"event_types": ["motion_detected", "alarm_triggered"],
|
|
"camera_ids": [1, 2, 3],
|
|
"severity": "warning"
|
|
}
|
|
}
|
|
|
|
// Event notification
|
|
{
|
|
"subscription_id": "uuid",
|
|
"event": { ...event object... },
|
|
"sequence_number": 42
|
|
}
|
|
|
|
// Heartbeat
|
|
{"action": "ping"}
|
|
{"action": "pong"}
|
|
```
|
|
|
|
**Reconnection:**
|
|
- Client implements exponential backoff
|
|
- Server buffers critical events for 5 minutes
|
|
- Reconnected clients receive missed critical events
|
|
operationId: streamEvents
|
|
security:
|
|
- BearerAuth: []
|
|
parameters:
|
|
- name: Connection
|
|
in: header
|
|
required: true
|
|
schema:
|
|
type: string
|
|
enum: [Upgrade]
|
|
- name: Upgrade
|
|
in: header
|
|
required: true
|
|
schema:
|
|
type: string
|
|
enum: [websocket]
|
|
responses:
|
|
'101':
|
|
description: Switching Protocols to WebSocket
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
'426':
|
|
description: Upgrade Required
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Error'
|
|
|
|
/api/v1/events:
|
|
get:
|
|
tags: [Events]
|
|
summary: Query event history
|
|
description: Retrieve historical events with filtering and pagination
|
|
operationId: queryEvents
|
|
security:
|
|
- BearerAuth: []
|
|
parameters:
|
|
- name: event_type
|
|
in: query
|
|
schema:
|
|
$ref: '#/components/schemas/EventType'
|
|
- name: camera_id
|
|
in: query
|
|
schema:
|
|
type: integer
|
|
- name: start_time
|
|
in: query
|
|
description: Filter events after this time (ISO 8601)
|
|
schema:
|
|
type: string
|
|
format: date-time
|
|
- name: end_time
|
|
in: query
|
|
description: Filter events before this time (ISO 8601)
|
|
schema:
|
|
type: string
|
|
format: date-time
|
|
- name: severity
|
|
in: query
|
|
schema:
|
|
$ref: '#/components/schemas/EventSeverity'
|
|
- name: acknowledged
|
|
in: query
|
|
description: Filter by acknowledgment status
|
|
schema:
|
|
type: boolean
|
|
- name: limit
|
|
in: query
|
|
schema:
|
|
type: integer
|
|
default: 50
|
|
minimum: 1
|
|
maximum: 1000
|
|
- name: offset
|
|
in: query
|
|
schema:
|
|
type: integer
|
|
default: 0
|
|
minimum: 0
|
|
responses:
|
|
'200':
|
|
description: Event list
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
events:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/Event'
|
|
total:
|
|
type: integer
|
|
limit:
|
|
type: integer
|
|
offset:
|
|
type: integer
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
|
|
/api/v1/events/{event_id}:
|
|
get:
|
|
tags: [Events]
|
|
summary: Get event details
|
|
description: Retrieve detailed information about a specific event
|
|
operationId: getEvent
|
|
security:
|
|
- BearerAuth: []
|
|
parameters:
|
|
- name: event_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
format: uuid
|
|
responses:
|
|
'200':
|
|
description: Event details
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Event'
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
'404':
|
|
$ref: '#/components/responses/NotFound'
|
|
|
|
patch:
|
|
tags: [Events]
|
|
summary: Acknowledge event
|
|
description: Mark an event as acknowledged by current user
|
|
operationId: acknowledgeEvent
|
|
security:
|
|
- BearerAuth: []
|
|
parameters:
|
|
- name: event_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
format: uuid
|
|
responses:
|
|
'200':
|
|
description: Event acknowledged
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Event'
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
'404':
|
|
$ref: '#/components/responses/NotFound'
|
|
|
|
# ============================================================================
|
|
# RECORDING ENDPOINTS
|
|
# ============================================================================
|
|
|
|
/api/v1/recordings:
|
|
get:
|
|
tags: [Recordings]
|
|
summary: Query recordings
|
|
description: Search for recorded video segments by time range and camera
|
|
operationId: queryRecordings
|
|
security:
|
|
- BearerAuth: []
|
|
parameters:
|
|
- name: camera_id
|
|
in: query
|
|
schema:
|
|
type: integer
|
|
- name: start_time
|
|
in: query
|
|
required: true
|
|
schema:
|
|
type: string
|
|
format: date-time
|
|
- name: end_time
|
|
in: query
|
|
required: true
|
|
schema:
|
|
type: string
|
|
format: date-time
|
|
- name: trigger
|
|
in: query
|
|
schema:
|
|
$ref: '#/components/schemas/RecordingTrigger'
|
|
- name: limit
|
|
in: query
|
|
schema:
|
|
type: integer
|
|
default: 50
|
|
- name: offset
|
|
in: query
|
|
schema:
|
|
type: integer
|
|
default: 0
|
|
responses:
|
|
'200':
|
|
description: Recording list
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
recordings:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/Recording'
|
|
total:
|
|
type: integer
|
|
'400':
|
|
$ref: '#/components/responses/BadRequest'
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
|
|
/api/v1/recordings/{recording_id}:
|
|
get:
|
|
tags: [Recordings]
|
|
summary: Get recording details
|
|
operationId: getRecording
|
|
security:
|
|
- BearerAuth: []
|
|
parameters:
|
|
- name: recording_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
format: uuid
|
|
responses:
|
|
'200':
|
|
description: Recording details
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Recording'
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
'404':
|
|
$ref: '#/components/responses/NotFound'
|
|
|
|
/api/v1/recordings/{recording_id}/export:
|
|
post:
|
|
tags: [Recordings]
|
|
summary: Export recording
|
|
description: |
|
|
Request video export for a recording segment.
|
|
Returns job ID for tracking export progress.
|
|
Completed exports available for download via provided URL.
|
|
operationId: exportRecording
|
|
security:
|
|
- BearerAuth: []
|
|
parameters:
|
|
- name: recording_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
format: uuid
|
|
requestBody:
|
|
required: false
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
format:
|
|
type: string
|
|
enum: [mp4, avi]
|
|
default: mp4
|
|
include_metadata:
|
|
type: boolean
|
|
default: true
|
|
responses:
|
|
'202':
|
|
description: Export job created
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
job_id:
|
|
type: string
|
|
format: uuid
|
|
status:
|
|
type: string
|
|
enum: [pending, processing]
|
|
estimated_completion:
|
|
type: string
|
|
format: date-time
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
'404':
|
|
$ref: '#/components/responses/NotFound'
|
|
|
|
/api/v1/recordings/capacity:
|
|
get:
|
|
tags: [Recordings]
|
|
summary: Get recording capacity
|
|
description: Returns storage capacity metrics for the ring buffer
|
|
operationId: getRecordingCapacity
|
|
security:
|
|
- BearerAuth: []
|
|
responses:
|
|
'200':
|
|
description: Capacity metrics
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
total_capacity_gb:
|
|
type: number
|
|
format: float
|
|
used_capacity_gb:
|
|
type: number
|
|
format: float
|
|
free_capacity_gb:
|
|
type: number
|
|
format: float
|
|
percent_used:
|
|
type: number
|
|
format: float
|
|
recording_depth_hours:
|
|
type: number
|
|
format: float
|
|
oldest_recording:
|
|
type: string
|
|
format: date-time
|
|
warnings:
|
|
type: array
|
|
items:
|
|
type: string
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
|
|
# ============================================================================
|
|
# ANALYTICS ENDPOINTS
|
|
# ============================================================================
|
|
|
|
/api/v1/analytics/{camera_id}:
|
|
get:
|
|
tags: [Analytics]
|
|
summary: Get analytics configuration
|
|
description: Returns current analytics configuration for a camera
|
|
operationId: getAnalyticsConfig
|
|
security:
|
|
- BearerAuth: []
|
|
parameters:
|
|
- $ref: '#/components/parameters/CameraId'
|
|
responses:
|
|
'200':
|
|
description: Analytics configurations
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/AnalyticsConfig'
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
'404':
|
|
$ref: '#/components/responses/NotFound'
|
|
|
|
put:
|
|
tags: [Analytics]
|
|
summary: Update analytics configuration
|
|
description: Configure video analytics for a camera
|
|
operationId: updateAnalyticsConfig
|
|
security:
|
|
- BearerAuth: []
|
|
parameters:
|
|
- $ref: '#/components/parameters/CameraId'
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/AnalyticsConfig'
|
|
responses:
|
|
'200':
|
|
description: Configuration updated
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/AnalyticsConfig'
|
|
'400':
|
|
description: Invalid configuration or unsupported analytics type
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Error'
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
'403':
|
|
$ref: '#/components/responses/Forbidden'
|
|
|
|
# ============================================================================
|
|
# SYSTEM ENDPOINTS
|
|
# ============================================================================
|
|
|
|
/api/v1/health:
|
|
get:
|
|
tags: [System]
|
|
summary: Health check
|
|
description: |
|
|
Returns API health status. No authentication required.
|
|
Used by load balancers and monitoring systems.
|
|
operationId: healthCheck
|
|
responses:
|
|
'200':
|
|
description: System healthy
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
status:
|
|
type: string
|
|
enum: [healthy, degraded, unhealthy]
|
|
checks:
|
|
type: object
|
|
properties:
|
|
api:
|
|
type: string
|
|
sdk_bridge:
|
|
type: string
|
|
redis:
|
|
type: string
|
|
geviserver:
|
|
type: string
|
|
timestamp:
|
|
type: string
|
|
format: date-time
|
|
'503':
|
|
description: System unhealthy
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
status:
|
|
type: string
|
|
enum: [unhealthy]
|
|
checks:
|
|
type: object
|
|
timestamp:
|
|
type: string
|
|
|
|
/api/v1/status:
|
|
get:
|
|
tags: [System]
|
|
summary: System status
|
|
description: Detailed system status and metrics (requires authentication)
|
|
operationId: getStatus
|
|
security:
|
|
- BearerAuth: []
|
|
responses:
|
|
'200':
|
|
description: Detailed status
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
uptime_seconds:
|
|
type: integer
|
|
active_streams:
|
|
type: integer
|
|
active_websocket_connections:
|
|
type: integer
|
|
cameras_online:
|
|
type: integer
|
|
cameras_total:
|
|
type: integer
|
|
sdk_version:
|
|
type: string
|
|
api_version:
|
|
type: string
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
|
|
# ==============================================================================
|
|
# COMPONENTS
|
|
# ==============================================================================
|
|
|
|
components:
|
|
securitySchemes:
|
|
BearerAuth:
|
|
type: http
|
|
scheme: bearer
|
|
bearerFormat: JWT
|
|
description: JWT token obtained from /auth/login endpoint
|
|
|
|
parameters:
|
|
CameraId:
|
|
name: camera_id
|
|
in: path
|
|
required: true
|
|
description: Camera channel ID
|
|
schema:
|
|
type: integer
|
|
minimum: 1
|
|
|
|
responses:
|
|
BadRequest:
|
|
description: Bad request - invalid parameters
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Error'
|
|
|
|
Unauthorized:
|
|
description: Unauthorized - missing or invalid authentication
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Error'
|
|
example:
|
|
error_code: UNAUTHORIZED
|
|
message: Missing or invalid authentication token
|
|
details: null
|
|
timestamp: "2025-12-08T14:30:00Z"
|
|
|
|
Forbidden:
|
|
description: Forbidden - insufficient permissions
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Error'
|
|
example:
|
|
error_code: FORBIDDEN
|
|
message: Insufficient permissions for this resource
|
|
details: null
|
|
timestamp: "2025-12-08T14:30:00Z"
|
|
|
|
NotFound:
|
|
description: Resource not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Error'
|
|
example:
|
|
error_code: NOT_FOUND
|
|
message: Resource not found
|
|
details: null
|
|
timestamp: "2025-12-08T14:30:00Z"
|
|
|
|
TooManyRequests:
|
|
description: Too many requests - rate limit exceeded
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Error'
|
|
example:
|
|
error_code: RATE_LIMIT_EXCEEDED
|
|
message: Too many requests, please try again later
|
|
details:
|
|
retry_after_seconds: 60
|
|
timestamp: "2025-12-08T14:30:00Z"
|
|
headers:
|
|
Retry-After:
|
|
description: Seconds until rate limit resets
|
|
schema:
|
|
type: integer
|
|
|
|
schemas:
|
|
# Authentication Schemas
|
|
LoginRequest:
|
|
type: object
|
|
required:
|
|
- username
|
|
- password
|
|
properties:
|
|
username:
|
|
type: string
|
|
minLength: 3
|
|
maxLength: 50
|
|
example: operator1
|
|
password:
|
|
type: string
|
|
format: password
|
|
minLength: 8
|
|
example: SecurePass123!
|
|
|
|
TokenPair:
|
|
type: object
|
|
properties:
|
|
access_token:
|
|
type: string
|
|
description: JWT access token (1 hour expiration)
|
|
refresh_token:
|
|
type: string
|
|
description: JWT refresh token (7 days expiration)
|
|
token_type:
|
|
type: string
|
|
enum: [bearer]
|
|
expires_in:
|
|
type: integer
|
|
description: Seconds until access token expires
|
|
example: 3600
|
|
|
|
RefreshTokenRequest:
|
|
type: object
|
|
required:
|
|
- refresh_token
|
|
properties:
|
|
refresh_token:
|
|
type: string
|
|
|
|
AccessToken:
|
|
type: object
|
|
properties:
|
|
access_token:
|
|
type: string
|
|
token_type:
|
|
type: string
|
|
enum: [bearer]
|
|
expires_in:
|
|
type: integer
|
|
|
|
# Camera Schemas
|
|
Camera:
|
|
type: object
|
|
properties:
|
|
id:
|
|
type: integer
|
|
description: Camera channel ID
|
|
example: 5
|
|
global_id:
|
|
type: string
|
|
format: uuid
|
|
description: GeViScope global identifier
|
|
name:
|
|
type: string
|
|
example: "Entrance Camera"
|
|
description:
|
|
type: string
|
|
nullable: true
|
|
example: "Main entrance monitoring"
|
|
location:
|
|
type: string
|
|
nullable: true
|
|
example: "Building A - Main Entrance"
|
|
status:
|
|
$ref: '#/components/schemas/CameraStatus'
|
|
capabilities:
|
|
$ref: '#/components/schemas/CameraCapabilities'
|
|
recording_status:
|
|
type: object
|
|
properties:
|
|
is_recording:
|
|
type: boolean
|
|
mode:
|
|
$ref: '#/components/schemas/RecordingTrigger'
|
|
start_time:
|
|
type: string
|
|
format: date-time
|
|
created_at:
|
|
type: string
|
|
format: date-time
|
|
updated_at:
|
|
type: string
|
|
format: date-time
|
|
|
|
CameraStatus:
|
|
type: string
|
|
enum: [online, offline, error, maintenance]
|
|
|
|
CameraCapabilities:
|
|
type: object
|
|
properties:
|
|
has_ptz:
|
|
type: boolean
|
|
has_video_sensor:
|
|
type: boolean
|
|
has_contrast_detection:
|
|
type: boolean
|
|
has_sync_detection:
|
|
type: boolean
|
|
supported_analytics:
|
|
type: array
|
|
items:
|
|
type: string
|
|
enum: [vmd, npr, obtrack, gtect, cpa]
|
|
supported_resolutions:
|
|
type: array
|
|
items:
|
|
type: string
|
|
example: "1920x1080"
|
|
supported_formats:
|
|
type: array
|
|
items:
|
|
type: string
|
|
enum: [h264, mjpeg]
|
|
|
|
# Stream Schemas
|
|
StreamRequest:
|
|
type: object
|
|
properties:
|
|
format:
|
|
type: string
|
|
enum: [h264, mjpeg]
|
|
default: h264
|
|
resolution:
|
|
type: string
|
|
nullable: true
|
|
example: "1920x1080"
|
|
fps:
|
|
type: integer
|
|
minimum: 1
|
|
maximum: 60
|
|
nullable: true
|
|
quality:
|
|
type: integer
|
|
minimum: 1
|
|
maximum: 100
|
|
default: 90
|
|
|
|
StreamResponse:
|
|
type: object
|
|
properties:
|
|
stream_id:
|
|
type: string
|
|
format: uuid
|
|
camera_id:
|
|
type: integer
|
|
stream_url:
|
|
type: string
|
|
format: uri
|
|
description: Token-authenticated stream URL
|
|
example: "http://localhost:7703/stream?channel=5&token=eyJhbGc..."
|
|
format:
|
|
type: string
|
|
resolution:
|
|
type: string
|
|
fps:
|
|
type: integer
|
|
expires_at:
|
|
type: string
|
|
format: date-time
|
|
|
|
# PTZ Schemas
|
|
PTZCommand:
|
|
type: object
|
|
required:
|
|
- action
|
|
properties:
|
|
action:
|
|
type: string
|
|
enum: [pan_left, pan_right, tilt_up, tilt_down, zoom_in, zoom_out, stop, goto_preset, save_preset]
|
|
speed:
|
|
type: integer
|
|
minimum: 1
|
|
maximum: 100
|
|
default: 50
|
|
nullable: true
|
|
preset_id:
|
|
type: integer
|
|
minimum: 1
|
|
maximum: 255
|
|
nullable: true
|
|
|
|
PTZPreset:
|
|
type: object
|
|
properties:
|
|
id:
|
|
type: integer
|
|
minimum: 1
|
|
maximum: 255
|
|
camera_id:
|
|
type: integer
|
|
name:
|
|
type: string
|
|
minLength: 1
|
|
maxLength: 50
|
|
pan:
|
|
type: integer
|
|
minimum: -180
|
|
maximum: 180
|
|
tilt:
|
|
type: integer
|
|
minimum: -90
|
|
maximum: 90
|
|
zoom:
|
|
type: integer
|
|
minimum: 0
|
|
maximum: 100
|
|
created_at:
|
|
type: string
|
|
format: date-time
|
|
created_by:
|
|
type: string
|
|
format: uuid
|
|
updated_at:
|
|
type: string
|
|
format: date-time
|
|
|
|
# Event Schemas
|
|
Event:
|
|
type: object
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
event_type:
|
|
$ref: '#/components/schemas/EventType'
|
|
camera_id:
|
|
type: integer
|
|
nullable: true
|
|
timestamp:
|
|
type: string
|
|
format: date-time
|
|
severity:
|
|
$ref: '#/components/schemas/EventSeverity'
|
|
data:
|
|
type: object
|
|
description: Type-specific event data
|
|
foreign_key:
|
|
type: string
|
|
nullable: true
|
|
acknowledged:
|
|
type: boolean
|
|
acknowledged_by:
|
|
type: string
|
|
format: uuid
|
|
nullable: true
|
|
acknowledged_at:
|
|
type: string
|
|
format: date-time
|
|
nullable: true
|
|
|
|
EventType:
|
|
type: string
|
|
enum:
|
|
- motion_detected
|
|
- object_tracked
|
|
- license_plate
|
|
- perimeter_breach
|
|
- camera_tamper
|
|
- camera_online
|
|
- camera_offline
|
|
- recording_started
|
|
- recording_stopped
|
|
- storage_warning
|
|
- alarm_triggered
|
|
- alarm_cleared
|
|
|
|
EventSeverity:
|
|
type: string
|
|
enum: [info, warning, error, critical]
|
|
|
|
# Recording Schemas
|
|
Recording:
|
|
type: object
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
camera_id:
|
|
type: integer
|
|
start_time:
|
|
type: string
|
|
format: date-time
|
|
end_time:
|
|
type: string
|
|
format: date-time
|
|
nullable: true
|
|
duration_seconds:
|
|
type: integer
|
|
nullable: true
|
|
file_size_bytes:
|
|
type: integer
|
|
nullable: true
|
|
trigger:
|
|
$ref: '#/components/schemas/RecordingTrigger'
|
|
status:
|
|
type: string
|
|
enum: [recording, completed, failed, exporting, exported]
|
|
export_url:
|
|
type: string
|
|
format: uri
|
|
nullable: true
|
|
metadata:
|
|
type: object
|
|
properties:
|
|
event_id:
|
|
type: string
|
|
format: uuid
|
|
nullable: true
|
|
pre_alarm_seconds:
|
|
type: integer
|
|
post_alarm_seconds:
|
|
type: integer
|
|
tags:
|
|
type: array
|
|
items:
|
|
type: string
|
|
notes:
|
|
type: string
|
|
nullable: true
|
|
created_at:
|
|
type: string
|
|
format: date-time
|
|
|
|
RecordingTrigger:
|
|
type: string
|
|
enum: [scheduled, event, manual, continuous]
|
|
|
|
# Analytics Schemas
|
|
AnalyticsConfig:
|
|
type: object
|
|
properties:
|
|
camera_id:
|
|
type: integer
|
|
analytics_type:
|
|
type: string
|
|
enum: [vmd, npr, obtrack, gtect, cpa]
|
|
enabled:
|
|
type: boolean
|
|
config:
|
|
type: object
|
|
description: Type-specific configuration
|
|
updated_at:
|
|
type: string
|
|
format: date-time
|
|
updated_by:
|
|
type: string
|
|
format: uuid
|
|
|
|
# Error Schema
|
|
Error:
|
|
type: object
|
|
properties:
|
|
error_code:
|
|
type: string
|
|
description: Machine-readable error code
|
|
message:
|
|
type: string
|
|
description: Human-readable error message
|
|
details:
|
|
type: object
|
|
nullable: true
|
|
description: Additional error details
|
|
timestamp:
|
|
type: string
|
|
format: date-time
|
|
|
|
security:
|
|
- BearerAuth: []
|