feat: dashboard screenshot upload & retention (last 20 per client)

- Listener: subscribe to dashboard topic, forward screenshots to API
- API: store latest + last 20 timestamped screenshots per client, auto-delete older files
- Docs: updated README, TECH-CHANGELOG, and copilot-instructions for screenshot upload and retention policy
This commit is contained in:
RobbStarkAustria
2025-11-30 13:38:07 +00:00
parent df9f29bc6a
commit c193209326
7 changed files with 273 additions and 14 deletions

View File

@@ -0,0 +1,94 @@
# Screenshot Transmission Implementation
## Overview
Clients send screenshots via MQTT during heartbeat intervals. The listener service receives these screenshots and forwards them to the server API for storage.
## Architecture
### MQTT Topic
- **Topic**: `infoscreen/{uuid}/screenshot`
- **Payload Format**:
- Raw binary image data (JPEG/PNG), OR
- JSON with base64-encoded image: `{"image": "<base64-string>"}`
### Components
#### 1. Listener Service (`listener/listener.py`)
- **Subscribes to**: `infoscreen/+/screenshot`
- **Function**: `handle_screenshot(uuid, payload)`
- Detects payload format (binary or JSON)
- Converts binary to base64 if needed
- Forwards to API via HTTP POST
#### 2. Server API (`server/routes/clients.py`)
- **Endpoint**: `POST /api/clients/<uuid>/screenshot`
- **Authentication**: No authentication required (internal service call)
- **Accepts**:
- JSON: `{"image": "<base64-encoded-image>"}`
- Binary: raw image data
- **Storage**:
- Saves to `server/screenshots/{uuid}_{timestamp}.jpg` (with timestamp)
- Saves to `server/screenshots/{uuid}.jpg` (latest, for quick retrieval)
#### 3. Retrieval (`server/wsgi.py`)
- **Endpoint**: `GET /screenshots/<uuid>`
- **Returns**: Latest screenshot for the given client UUID
- **Nginx**: Exposes `/screenshots/{uuid}.jpg` in production
## Unified Identification Method
Screenshots are identified by **client UUID**:
- Each client has a unique UUID stored in the `clients` table
- Screenshots are stored as `{uuid}.jpg` (latest) and `{uuid}_{timestamp}.jpg` (historical)
- The API endpoint requires UUID validation against the database
- Retrieval is done via `GET /screenshots/<uuid>` which returns the latest screenshot
## Data Flow
```
Client → MQTT (infoscreen/{uuid}/screenshot)
Listener Service
↓ (validates client exists)
↓ (converts binary → base64 if needed)
API POST /api/clients/{uuid}/screenshot
↓ (validates client UUID)
↓ (decodes base64 → binary)
Filesystem: server/screenshots/{uuid}.jpg
Dashboard/Nginx: GET /screenshots/{uuid}
```
## Configuration
### Environment Variables
- **Listener**: `API_BASE_URL` (default: `http://server:8000`)
- **Server**: Screenshots stored in `server/screenshots/` directory
### Dependencies
- Listener: Added `requests>=2.31.0` to `listener/requirements.txt`
- Server: Uses built-in Flask and base64 libraries
## Error Handling
- **Client Not Found**: Returns 404 if UUID doesn't exist in database
- **Invalid Payload**: Returns 400 if image data is missing or invalid
- **API Timeout**: Listener logs error and continues (timeout: 10s)
- **Network Errors**: Listener logs and continues operation
## Security Considerations
- Screenshot endpoint does not require authentication (internal service-to-service)
- Client UUID must exist in database before screenshot is accepted
- Base64 encoding prevents binary data issues in JSON transport
- File size is tracked and logged for monitoring
## Future Enhancements
- Add screenshot retention policy (auto-delete old timestamped files)
- Add compression before transmission
- Add screenshot quality settings
- Add authentication between listener and API
- Add screenshot history API endpoint