Dashboard Add top-right user dropdown using Syncfusion DropDownButton: shows username + role; menu entries “Profil” and “Abmelden”. Replace custom dropdown logic with Syncfusion component; position at header’s right edge. Update /logout page to call backend logout and redirect to /login (reliable user switching). Build/Config Add @syncfusion/ej2-react-splitbuttons and @syncfusion/ej2-splitbuttons dependencies. Update Vite optimizeDeps.include to pre-bundle splitbuttons and avoid import-analysis errors. Docs README: Rework Architecture Overview with clearer data flow: Listener consumes MQTT (discovery/heartbeats) and updates API. Scheduler reads from API and publishes events via MQTT to clients. Clients send via MQTT and receive via MQTT. Worker receives commands directly from API and reports results back (no MQTT). Explicit note: MariaDB is accessed exclusively by the API Server; Dashboard never talks to DB directly. README: Add SplitButtons to “Syncfusion Components Used”; add troubleshooting steps for @syncfusion/ej2-react-splitbuttons import issues (optimizeDeps + volume reset). Copilot instructions: Document header user menu and splitbuttons technical notes (deps, optimizeDeps, dev-container node_modules volume). Program info Bump to 2025.1.0-alpha.10 with changelog: UI: Header user menu (DropDownButton with username/role; Profil/Abmelden). Frontend: Syncfusion SplitButtons integration + Vite pre-bundling config. Fix: Added README guidance for splitbuttons import errors. No breaking changes.
497 lines
20 KiB
Markdown
497 lines
20 KiB
Markdown
# Infoscreen 2025
|
||
|
||
[](https://www.docker.com/)
|
||
[](https://reactjs.org/)
|
||
[](https://flask.palletsprojects.com/)
|
||
[](https://mariadb.org/)
|
||
[](https://mosquitto.org/)
|
||
|
||
A comprehensive multi-service digital signage solution for educational institutions, featuring client management, event scheduling, presentation conversion, and real-time MQTT communication.
|
||
|
||
## 🏗️ Architecture Overview
|
||
|
||
```
|
||
┌───────────────┐ ┌──────────────────────────┐ ┌───────────────┐
|
||
│ Dashboard │◄──────►│ API Server │◄──────►│ Worker │
|
||
│ (React/Vite) │ │ (Flask) │ │ (Conversions) │
|
||
└───────────────┘ └──────────────────────────┘ └───────────────┘
|
||
▲ ▲
|
||
│ │
|
||
┌───────────────┐ │
|
||
│ MariaDB │ │
|
||
│ (Database) │ │
|
||
└───────────────┘ │
|
||
│ direct commands/results
|
||
|
||
Reads events ▲ Interacts with API ▲
|
||
│ ┌────┘
|
||
┌───────────────┐ │ │ ┌───────────────┐
|
||
│ Scheduler │──┘ └──│ Listener │
|
||
│ (Events) │ │ (MQTT Client) │
|
||
└───────────────┘ └───────────────┘
|
||
│ Publishes events ▲ Consumes discovery/heartbeats
|
||
▼ │ and forwards to API
|
||
┌─────────────────┐◄─────────────────────────────────────────────────────────────────┘
|
||
│ MQTT Broker │────────────────────────────────────────────────────────► Clients
|
||
│ (Mosquitto) │ Sends events to clients (send discovery/heartbeats)
|
||
└─────────────────┘
|
||
```
|
||
|
||
Data flow summary:
|
||
- Listener: consumes discovery and heartbeat messages from the MQTT Broker and updates the API Server (client registration/heartbeats).
|
||
- Scheduler: reads events from the API Server and publishes active content to the MQTT Broker (retained topics per group) for clients.
|
||
- Clients: send discovery/heartbeat via the MQTT Broker (handled by the Listener) and receive content from the Scheduler via MQTT.
|
||
- Worker: receives conversion commands directly from the API Server and reports results/status back to the API (no MQTT involved).
|
||
- MariaDB: is accessed exclusively by the API Server. The Dashboard never talks to the database directly; it only communicates with the API.
|
||
|
||
## 🌟 Key Features
|
||
|
||
|
||
- Modern React-based web interface with Syncfusion components
|
||
- Real-time client monitoring and group management
|
||
- Event scheduling with academic period support
|
||
- Media management with presentation conversion
|
||
- Holiday calendar integration
|
||
- Visual indicators: TentTree icon next to the main event icon marks events that skip holidays (icon color: black)
|
||
|
||
- **Event Deletion**: All event types (single, single-in-series, entire series) are handled with custom dialogs. The frontend intercepts Syncfusion's built-in RecurrenceAlert and DeleteAlert popups to provide a unified, user-friendly deletion flow:
|
||
- Single (non-recurring) event: deleted directly after confirmation.
|
||
- Single occurrence of a recurring series: user can delete just that instance.
|
||
- Entire recurring series: user can delete all occurrences after a final custom confirmation dialog.
|
||
- Detached occurrences (edited/broken out): treated as single events.
|
||
|
||
### 🎯 **Event System**
|
||
- **Presentations**: PowerPoint/LibreOffice → PDF conversion via Gotenberg
|
||
- **Websites**: URL-based content display
|
||
- **Videos**: Media file streaming
|
||
- **Messages**: Text announcements
|
||
- **WebUntis**: Educational schedule integration
|
||
- **Recurrence & Holidays**: Recurring events can be configured to skip holidays. The backend generates EXDATEs (RecurrenceException) for holiday occurrences using RFC 5545 timestamps (yyyyMMddTHHmmssZ), so the calendar never shows those instances. The "Termine an Ferientagen erlauben" toggle does not affect these events.
|
||
- **Single Occurrence Editing**: Users can edit individual occurrences of recurring events without affecting the master series. The system provides a confirmation dialog to choose between editing a single occurrence or the entire series.
|
||
|
||
### 🏫 **Academic Period Management**
|
||
- Support for school years, semesters, and trimesters
|
||
- Austrian school system integration
|
||
- Holiday calendar synchronization
|
||
- Period-based event organization
|
||
|
||
### 📡 **Real-time Communication**
|
||
- MQTT-based client discovery and heartbeat monitoring
|
||
- Retained topics for reliable state synchronization
|
||
- WebSocket support for browser clients
|
||
- Automatic client group assignment
|
||
|
||
### 🔄 **Background Processing**
|
||
- Redis-based job queues for presentation conversion
|
||
- Gotenberg integration for LibreOffice/PowerPoint processing
|
||
- Asynchronous file processing with status tracking
|
||
- RQ (Redis Queue) worker management
|
||
|
||
## 🚀 Quick Start
|
||
|
||
### Prerequisites
|
||
- Docker & Docker Compose
|
||
- Git
|
||
- SSL certificates (for production)
|
||
|
||
### Development Setup
|
||
|
||
1. **Clone the repository**
|
||
```bash
|
||
git clone https://github.com/RobbStarkAustria/infoscreen_2025.git
|
||
cd infoscreen_2025
|
||
```
|
||
|
||
2. **Environment Configuration**
|
||
```bash
|
||
cp .env.example .env
|
||
# Edit .env with your configuration
|
||
```
|
||
|
||
3. **Start the development stack**
|
||
```bash
|
||
make up
|
||
# or: docker compose up -d --build
|
||
```
|
||
|
||
4. **Initialize the database (first run only)**
|
||
```bash
|
||
# One-shot: runs all Alembic migrations, creates default admin/group, and seeds academic periods
|
||
python server/initialize_database.py
|
||
```
|
||
|
||
5. **Access the services**
|
||
- Dashboard: http://localhost:5173
|
||
- API: http://localhost:8000
|
||
- Database: localhost:3306
|
||
- MQTT: localhost:1883 (WebSocket: 9001)
|
||
|
||
### Production Deployment
|
||
|
||
1. **Build and push images**
|
||
```bash
|
||
make build
|
||
make push
|
||
```
|
||
|
||
2. **Deploy on server**
|
||
```bash
|
||
make pull-prod
|
||
make up-prod
|
||
```
|
||
|
||
For detailed deployment instructions, see:
|
||
- [Debian Deployment Guide](deployment-debian.md)
|
||
- [Ubuntu Deployment Guide](deployment-ubuntu.md)
|
||
|
||
## 🛠️ Services
|
||
|
||
### 🖥️ **Dashboard** (`dashboard/`)
|
||
- **Technology**: React 19 + TypeScript + Vite
|
||
- **UI Framework**: Syncfusion components (Material 3 theme)
|
||
- **Styling**: Centralized Syncfusion Material 3 CSS imports in `dashboard/src/main.tsx`
|
||
- **Features**: Responsive design, real-time updates, file management
|
||
- **Port**: 5173 (dev), served via Nginx (prod)
|
||
- **Data access**: No direct database connection; communicates with the API Server only via HTTP.
|
||
|
||
### 🔧 **API Server** (`server/`)
|
||
- **Technology**: Flask + SQLAlchemy + Alembic
|
||
- **Database**: MariaDB with timezone-aware timestamps
|
||
- **Features**: RESTful API, file uploads, MQTT integration
|
||
- Recurrence/holidays: returns only master events with `RecurrenceRule` and `RecurrenceException` (EXDATEs) so clients render recurrences and skip holiday instances reliably.
|
||
- Single occurrence detach: `POST /api/events/<id>/occurrences/<date>/detach` creates standalone events from recurring series without modifying the master event.
|
||
- **Port**: 8000
|
||
- **Health Check**: `/health`
|
||
|
||
### 👂 **Listener** (`listener/`)
|
||
- **Technology**: Python + paho-mqtt
|
||
- **Purpose**: MQTT message processing, client discovery
|
||
- **Features**: Heartbeat monitoring, automatic client registration
|
||
|
||
### ⏰ **Scheduler** (`scheduler/`)
|
||
- **Technology**: Python + SQLAlchemy
|
||
- **Purpose**: Event publishing, group-based content distribution
|
||
- **Features**: Time-based event activation, MQTT publishing
|
||
|
||
### 🔄 **Worker** (Conversion Service)
|
||
- **Technology**: RQ (Redis Queue) + Gotenberg
|
||
- **Purpose**: Background presentation conversion
|
||
- **Features**: PPT/PPTX/ODP → PDF conversion, status tracking
|
||
|
||
### 🗄️ **Database** (MariaDB 11.2)
|
||
- **Features**: Health checks, automatic initialization
|
||
- **Migrations**: Alembic-based schema management
|
||
- **Timezone**: UTC-aware timestamps
|
||
|
||
### 📡 **MQTT Broker** (Eclipse Mosquitto 2.0.21)
|
||
- **Features**: WebSocket support, health monitoring
|
||
- **Topics**:
|
||
- `infoscreen/discovery` - Client registration
|
||
- `infoscreen/{uuid}/heartbeat` - Client alive status
|
||
- `infoscreen/events/{group_id}` - Event distribution
|
||
- `infoscreen/{uuid}/group_id` - Client group assignment
|
||
|
||
## 📁 Project Structure
|
||
|
||
```
|
||
infoscreen_2025/
|
||
├── dashboard/ # React frontend
|
||
│ ├── src/ # React components and logic
|
||
│ ├── public/ # Static assets
|
||
│ └── Dockerfile # Production build
|
||
├── server/ # Flask API backend
|
||
│ ├── routes/ # API endpoints
|
||
│ ├── alembic/ # Database migrations
|
||
│ ├── media/ # File storage
|
||
│ ├── initialize_database.py # All-in-one DB initialization (dev)
|
||
│ └── worker.py # Background jobs
|
||
├── listener/ # MQTT listener service
|
||
├── scheduler/ # Event scheduling service
|
||
├── models/ # Shared database models
|
||
├── mosquitto/ # MQTT broker configuration
|
||
├── certs/ # SSL certificates
|
||
├── docker-compose.yml # Development setup
|
||
├── docker-compose.prod.yml # Production setup
|
||
└── Makefile # Development shortcuts
|
||
```
|
||
|
||
## 🔧 Development
|
||
|
||
### Available Commands
|
||
|
||
```bash
|
||
# Development
|
||
make up # Start dev stack
|
||
make down # Stop dev stack
|
||
make logs # View all logs
|
||
make logs-server # View specific service logs
|
||
|
||
# Building & Deployment
|
||
make build # Build all images
|
||
make push # Push to registry
|
||
make pull-prod # Pull production images
|
||
make up-prod # Start production stack
|
||
|
||
# Maintenance
|
||
make health # Health checks
|
||
make fix-perms # Fix file permissions
|
||
```
|
||
|
||
### Database Management
|
||
|
||
```bash
|
||
# One-shot initialization (schema + defaults + academic periods)
|
||
python server/initialize_database.py
|
||
|
||
# Access database directly
|
||
docker exec -it infoscreen-db mysql -u${DB_USER} -p${DB_PASSWORD} ${DB_NAME}
|
||
|
||
# Run migrations
|
||
docker exec -it infoscreen-api alembic upgrade head
|
||
|
||
# Initialize academic periods (Austrian school system)
|
||
docker exec -it infoscreen-api python init_academic_periods.py
|
||
```
|
||
|
||
### MQTT Testing
|
||
|
||
```bash
|
||
# Subscribe to all topics
|
||
mosquitto_sub -h localhost -t "infoscreen/#" -v
|
||
|
||
# Publish test message
|
||
mosquitto_pub -h localhost -t "infoscreen/test" -m "Hello World"
|
||
|
||
# Monitor client heartbeats
|
||
mosquitto_sub -h localhost -t "infoscreen/+/heartbeat" -v
|
||
```
|
||
|
||
## 🌐 API Endpoints
|
||
|
||
### Core Resources
|
||
- `GET /api/clients` - List all registered clients
|
||
- `PUT /api/clients/{uuid}/group` - Assign client to group
|
||
- `GET /api/groups` - List client groups with alive status
|
||
- `GET /api/events` - List events with filtering
|
||
- `POST /api/events` - Create new event
|
||
- `POST /api/events/{id}/occurrences/{date}/detach` - Detach single occurrence from recurring series
|
||
- `GET /api/academic_periods` - List academic periods
|
||
- `POST /api/academic_periods/active` - Set active period
|
||
|
||
### File Management
|
||
- `POST /api/files` - Upload media files
|
||
- `GET /api/files/{path}` - Download files
|
||
- `GET /api/files/converted/{path}` - Download converted PDFs
|
||
- `POST /api/conversions/{media_id}/pdf` - Request conversion
|
||
- `GET /api/conversions/{media_id}/status` - Check conversion status
|
||
|
||
### Health & Monitoring
|
||
- `GET /health` - Service health check
|
||
- `GET /api/screenshots/{uuid}.jpg` - Client screenshots
|
||
|
||
## 🎨 Frontend Features
|
||
|
||
### Recurrence & holidays
|
||
- Recurrence is handled natively by Syncfusion. The API returns master events with `RecurrenceRule` and `RecurrenceException` (EXDATE) in RFC 5545 format (yyyyMMddTHHmmssZ, UTC) so the Scheduler excludes holiday instances reliably.
|
||
- Events with "skip holidays" display a TentTree icon next to the main event icon (icon color: black). The Scheduler’s native lower-right recurrence badge indicates series membership.
|
||
- Single occurrence editing: Users can edit either a single occurrence or the entire series. The UI persists changes using `onActionCompleted (requestType='eventChanged')`:
|
||
- Single occurrence → `POST /api/events/<id>/occurrences/<date>/detach` (creates standalone event and adds EXDATE to master)
|
||
- Series/single event → `PUT /api/events/<id>`
|
||
|
||
### Syncfusion Components Used (Material 3)
|
||
- **Schedule**: Event calendar with drag-drop support
|
||
- **Grid**: Data tables with filtering and sorting
|
||
- **DropDownList**: Group and period selectors
|
||
- **FileManager**: Media upload and organization
|
||
- **Kanban**: Task management views
|
||
- **Notifications**: Toast messages and alerts
|
||
- **Pager**: Used on Programinfo changelog for pagination
|
||
- **Cards (layouts)**: Programinfo sections styled with Syncfusion card classes
|
||
- **SplitButtons**: Header user menu (top-right) using Syncfusion DropDownButton to show current user and role, with actions “Profil” and “Abmelden”.
|
||
|
||
### Pages Overview
|
||
- **Dashboard**: System overview and statistics
|
||
- **Clients**: Device management and monitoring
|
||
- **Groups**: Client group organization
|
||
- **Events**: Schedule management
|
||
- **Media**: File upload and conversion
|
||
- **Settings**: System configuration
|
||
- **Holidays**: Academic calendar management
|
||
- **Program info**: Version, build info, tech stack and paginated changelog (reads `dashboard/public/program-info.json`)
|
||
|
||
## 🔒 Security & Authentication
|
||
|
||
- **Environment Variables**: Sensitive data via `.env`
|
||
- **SSL/TLS**: HTTPS support with custom certificates
|
||
- **MQTT Security**: Username/password authentication
|
||
- **Database**: Parameterized queries, connection pooling
|
||
- **File Uploads**: Type validation, size limits
|
||
- **CORS**: Configured for production deployment
|
||
|
||
## 📊 Monitoring & Logging
|
||
|
||
### Health Checks
|
||
All services include Docker health checks:
|
||
- API: HTTP endpoint monitoring
|
||
- Database: Connection and initialization status
|
||
- MQTT: Pub/sub functionality test
|
||
- Dashboard: Nginx availability
|
||
|
||
### Logging Strategy
|
||
- **Development**: Docker Compose logs with service prefixes
|
||
- **Production**: Centralized logging via Docker log drivers
|
||
- **MQTT**: Message-level debugging available
|
||
- **Database**: Query logging in development mode
|
||
|
||
## 🌍 Deployment Options
|
||
|
||
### Development
|
||
- **Hot Reload**: Vite dev server + Flask debug mode
|
||
- **Volume Mounts**: Live code editing
|
||
- **Debug Ports**: Python debugger support (port 5678)
|
||
- **Local Certificates**: Self-signed SSL for testing
|
||
|
||
### Production
|
||
- **Optimized Builds**: Multi-stage Dockerfiles
|
||
- **Reverse Proxy**: Nginx with SSL termination
|
||
- **Health Monitoring**: Comprehensive healthchecks
|
||
- **Registry**: GitHub Container Registry integration
|
||
- **Scaling**: Docker Compose for single-node deployment
|
||
|
||
## 🤝 Contributing
|
||
|
||
1. Fork the repository
|
||
2. Create a feature branch: `git checkout -b feature/amazing-feature`
|
||
3. Commit your changes: `git commit -m 'Add amazing feature'`
|
||
4. Push to the branch: `git push origin feature/amazing-feature`
|
||
5. Open a Pull Request
|
||
|
||
### Development Guidelines
|
||
- Follow existing code patterns and naming conventions
|
||
- Add appropriate tests for new features
|
||
- Update documentation for API changes
|
||
- Use TypeScript for frontend development
|
||
- Follow Python PEP 8 for backend code
|
||
|
||
## 📋 Requirements
|
||
|
||
### System Requirements
|
||
- **CPU**: 2+ cores recommended
|
||
- **RAM**: 4GB minimum, 8GB recommended
|
||
- **Storage**: 20GB+ for media files and database
|
||
- **Network**: Reliable internet for client communication
|
||
|
||
### Software Dependencies
|
||
- Docker 24.0+
|
||
- Docker Compose 2.0+
|
||
- Git 2.30+
|
||
- Modern web browser (Chrome, Firefox, Safari, Edge)
|
||
|
||
## 🐛 Troubleshooting
|
||
|
||
### Common Issues
|
||
|
||
**Services won't start**
|
||
```bash
|
||
# Check service health
|
||
make health
|
||
|
||
# View specific service logs
|
||
make logs-server
|
||
make logs-db
|
||
```
|
||
|
||
**Database connection errors**
|
||
```bash
|
||
# Verify database is running
|
||
docker exec -it infoscreen-db mysqladmin ping
|
||
|
||
# Check credentials in .env file
|
||
# Restart dependent services
|
||
```
|
||
|
||
**MQTT communication issues**
|
||
**Vite import-analysis errors (Syncfusion splitbuttons)**
|
||
```bash
|
||
# Symptom
|
||
[plugin:vite:import-analysis] Failed to resolve import "@syncfusion/ej2-react-splitbuttons"
|
||
|
||
# Fix
|
||
# 1) Ensure dependencies are added in dashboard/package.json:
|
||
# - @syncfusion/ej2-react-splitbuttons, @syncfusion/ej2-splitbuttons
|
||
# 2) In dashboard/vite.config.ts, add to optimizeDeps.include:
|
||
# '@syncfusion/ej2-react-splitbuttons', '@syncfusion/ej2-splitbuttons'
|
||
# 3) If dashboard uses a named node_modules volume, recreate it so npm ci runs inside the container:
|
||
docker compose rm -sf dashboard
|
||
docker volume rm <project>_dashboard-node-modules <project>_dashboard-vite-cache || true
|
||
docker compose up -d --build dashboard
|
||
```
|
||
```bash
|
||
# Test MQTT broker
|
||
mosquitto_pub -h localhost -t test -m "hello"
|
||
|
||
# Check client certificates and credentials
|
||
# Verify firewall settings for ports 1883/9001
|
||
```
|
||
|
||
**File conversion problems**
|
||
```bash
|
||
# Check Gotenberg service
|
||
curl http://localhost:3000/health
|
||
|
||
# Monitor worker logs
|
||
make logs-worker
|
||
|
||
# Check Redis queue status
|
||
docker exec -it infoscreen-redis redis-cli LLEN conversions
|
||
```
|
||
|
||
## 📄 License
|
||
|
||
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
||
|
||
## 🙏 Acknowledgments
|
||
|
||
- **Syncfusion**: UI components for React dashboard
|
||
- **Eclipse Mosquitto**: MQTT broker implementation
|
||
- **Gotenberg**: Document conversion service
|
||
- **MariaDB**: Reliable database engine
|
||
- **Flask**: Python web framework
|
||
- **React**: Frontend user interface library
|
||
|
||
---
|
||
|
||
For detailed technical documentation, deployment guides, and API specifications, please refer to the additional documentation files in this repository.
|
||
|
||
Notes:
|
||
- Tailwind CSS was removed. Styling is managed via Syncfusion Material 3 theme imports in `dashboard/src/main.tsx`.
|
||
|
||
## 🧭 Changelog Style Guide
|
||
|
||
When adding entries to `dashboard/public/program-info.json` (displayed on the Program info page):
|
||
|
||
- Structure per release
|
||
- `version` (e.g., `2025.1.0-alpha.8`)
|
||
- `date` in `YYYY-MM-DD` (ISO format)
|
||
- `changes`: array of short bullet strings
|
||
|
||
- Categories (Keep a Changelog inspired)
|
||
- Prefer starting bullets with an implicit category or an emoji, e.g.:
|
||
- Added (🆕/✨), Changed (🔧/🛠️), Fixed (🐛/✅), Removed (🗑️), Security (🔒), Deprecated (⚠️)
|
||
|
||
- Writing rules
|
||
- Keep bullets concise (ideally one line) and user-facing; avoid internal IDs or jargon
|
||
- Put the affected area first when helpful (e.g., “UI: …”, “API: …”, “Scheduler: …”)
|
||
- Highlight breaking changes with “BREAKING:”
|
||
- Prefer German wording consistently; dates are localized at runtime for display
|
||
|
||
- Ordering and size
|
||
- Newest release first in the array
|
||
- Aim for ≤ 8–10 bullets per release; group or summarize if longer
|
||
|
||
- JSON hygiene
|
||
- Valid JSON only (no trailing commas); escape quotes as needed
|
||
- One release object per version; do not modify historical entries unless to correct typos
|
||
|
||
The Program info page paginates older entries (default page size 5). Keep highlights at the top of each release for scanability.
|