Files
infoscreen/TECH-CHANGELOG.md
Olaf 03e3c11e90 feat: crash recovery, service_failed monitoring, broker health fields, command expiry sweep
- Add GET /api/clients/crashed endpoint (process_status=crashed or stale heartbeat)
- Add restart_app command action with same lifecycle + lockout as reboot_host
- Scheduler: crash auto-recovery loop (CRASH_RECOVERY_ENABLED flag, lockout, MQTT publish)
- Scheduler: unconditional command expiry sweep per poll cycle (sweep_expired_commands)
- Listener: subscribe to infoscreen/+/service_failed; persist service_failed_at + unit
- Listener: extract broker_connection block from health payload; persist reconnect_count + last_disconnect_at
- DB migration b1c2d3e4f5a6: service_failed_at, service_failed_unit, mqtt_reconnect_count, mqtt_last_disconnect_at on clients
- Add GET /api/clients/service_failed and POST /api/clients/<uuid>/clear_service_failed
- Monitoring overview API: include mqtt_reconnect_count + mqtt_last_disconnect_at per client
- Frontend: orange service-failed alert panel (hidden when empty, auto-refresh, quittieren action)
- Frontend: MQTT reconnect count + last disconnect in client detail panel
- MQTT auth hardening: listener/scheduler/server use env credentials; broker enforces allow_anonymous false
- Client command lifecycle foundation: ClientCommand model, reboot_host/shutdown_host, full ACK lifecycle
- Docs: TECH-CHANGELOG, DEV-CHANGELOG, MQTT_EVENT_PAYLOAD_GUIDE, copilot-instructions updated
- Add implementation-plans/, RESTART_VALIDATION_CHECKLIST.md, TODO.md
2026-04-05 10:17:56 +00:00

39 KiB
Raw Permalink Blame History

TECH-CHANGELOG

This changelog documents technical and developer-relevant changes included in public releases. For development workspace changes, see DEV-CHANGELOG.md. Not all changes here are reflected in the user-facing changelog (program-info.json), and not all UI/feature changes are repeated here. Some changes (e.g., backend refactoring, API adjustments, infrastructure, developer tooling, or internal logic) may only appear in TECH-CHANGELOG.md. For UI/feature changes, see dashboard/public/program-info.json.

Unreleased

  • <EFBFBD> Crash detection, auto-recovery, and service_failed monitoring (2026-04-05):
    • Added GET /api/clients/crashed endpoint: returns active clients with process_status=crashed or stale heartbeat beyond grace period, with crash_reason field.
    • Added restart_app command action alongside existing reboot_host/shutdown_host; registered in server/routes/clients.py with same safety lockout.
    • Scheduler: Added crash auto-recovery loop (feature-flagged via CRASH_RECOVERY_ENABLED): scans candidates via get_crash_recovery_candidates(), issues reboot_host command per client, publishes to primary + compat MQTT topics, updates command lifecycle.
    • Scheduler: Added unconditional command expiry sweep each poll cycle via sweep_expired_commands() in scheduler/db_utils.py: marks non-terminal ClientCommand rows with expires_at < now as expired.
    • Added service_failed topic ingestion in listener/listener.py: subscribe to infoscreen/+/service_failed on every connect; persist service_failed_at and service_failed_unit on Client; empty payload (retain clear) ignored.
    • Added broker_connection block extraction in health payload handler: persists mqtt_reconnect_count and mqtt_last_disconnect_at from infoscreen/{uuid}/health.
    • Added four new DB columns to clients table via migration b1c2d3e4f5a6: service_failed_at, service_failed_unit, mqtt_reconnect_count, mqtt_last_disconnect_at.
    • Added GET /api/clients/service_failed endpoint: lists clients with service_failed_at set, ordered by event time desc.
    • Added POST /api/clients/<uuid>/clear_service_failed endpoint: clears DB flag and publishes empty retained MQTT message to clear infoscreen/{uuid}/service_failed.
    • Monitoring overview API (GET /api/client-logs/monitoring-overview) now includes mqtt_reconnect_count and mqtt_last_disconnect_at per client.
    • Frontend: Added orange service-failed alert panel to monitoring page (hidden when empty, auto-refresh 15s, per-row Quittieren button with loading/success/error states).
    • Frontend: Client detail panel in monitoring now shows MQTT reconnect count and last disconnect timestamp.
    • Frontend: Added ServiceFailedClient, ServiceFailedClientsResponse types; fetchServiceFailedClients() and clearServiceFailed() API helpers in dashboard/src/apiClients.ts.
    • Added service_failed topic contract to MQTT_EVENT_PAYLOAD_GUIDE.md.
  • <EFBFBD>🔐 MQTT auth hardening for server-side services (2026-04-03):
    • listener/listener.py now uses env-based broker connectivity for host/port and credentials (MQTT_BROKER_HOST, MQTT_BROKER_PORT, MQTT_USER, MQTT_PASSWORD) instead of anonymous fixed mqtt:1883.
    • scheduler/scheduler.py now uses the same env-based MQTT auth path and optional TLS toggles (MQTT_TLS_ENABLED, MQTT_TLS_CA_CERT, MQTT_TLS_CERTFILE, MQTT_TLS_KEYFILE, MQTT_TLS_INSECURE).
    • docker-compose.yml and docker-compose.override.yml now pass MQTT credentials into listener and scheduler containers for consistent authenticated connections.
    • Mosquitto is now configured for authenticated access (allow_anonymous false, password_file /mosquitto/config/passwd) and bootstraps credentials from env at container startup.
    • MQTT healthcheck publish now authenticates with configured broker credentials.
  • 🔁 Client command lifecycle foundation (restart/shutdown) (2026-04-03):
    • Added persistent command tracking model ClientCommand in models/models.py and Alembic migration aa12bb34cc56_add_client_commands_table.py.
    • Upgraded POST /api/clients/<uuid>/restart from fire-and-forget publish to lifecycle-aware command issuance with command metadata (command_id, issued_at, expires_at, reason, requested_by).
    • Added POST /api/clients/<uuid>/shutdown endpoint with the same lifecycle contract.
    • Added GET /api/clients/commands/<command_id> status endpoint for command-state polling.
    • Added restart safety lockout in API path: max 3 restart commands per client in rolling 15 minutes, returning blocked_safety when threshold is exceeded.
    • Added command MQTT publish to infoscreen/{uuid}/commands (QoS1, non-retained) and temporary legacy restart compatibility publish to clients/{uuid}/restart.
    • Added temporary topic compatibility publish to infoscreen/{uuid}/command and listener acceptance of infoscreen/{uuid}/command/ack to bridge singular/plural naming assumptions.
    • Canonicalized command payload action values to host-level semantics: reboot_host and shutdown_host (API routes remain /restart and /shutdown for operator UX compatibility).
    • Added frozen payload validation snippets for integration/client tooling in implementation-plans/reboot-command-payload-schemas.md and implementation-plans/reboot-command-payload-schemas.json.
    • Listener now subscribes to infoscreen/{uuid}/commands/ack and maps client acknowledgements into command lifecycle states (ack_received, execution_started, completed, failed).
    • Initial lifecycle statuses implemented server-side: queued, publish_in_progress, published, failed, and blocked_safety.
    • Frontend API helper extended in dashboard/src/apiClients.ts with ClientCommand typing plus command APIs for shutdown and status polling preparation.

2026.1.0-alpha.16 (2026-04-02)

  • 🐛 Dashboard holiday banner refactoring and state fix (dashboard/src/dashboard.tsx):
    • Motivation — unstable fetch function: loadHolidayStatus had location.pathname in its useCallback dependency array, causing a new function reference to be created on every navigation event. The useEffect depending on that reference then re-fired, producing overlapping API calls at mount that cancelled each other via the request-sequence guard, leaving the banner unresolved.
    • Refactoring: Removed location.pathname from useCallback deps (it was unused inside the function body). The callback now has an empty dependency array, making its reference stable across the component lifetime. The useEffect is keyed only to the stable callback reference — no spurious re-fires.
    • Motivation — Syncfusion stale render: Syncfusion's MessageComponent caches its rendered DOM internally and does not reactively update when React passes new children or props. Even after React state changed, the component displayed whatever text was rendered on first mount.
    • Fix: Added a key prop derived from ${severity}:${text} to MessageComponent. React unmounts and remounts the component whenever the key changes, bypassing Syncfusion's internal caching and ensuring the correct message is always visible.
    • Result: Active-period name and holiday overlap details now render correctly on hard refresh, initial load, and route transitions without additional API calls.
  • 🗓️ Academic period bootstrap hardening (server/init_academic_periods.py):
    • Refactored initialization into idempotent flow:
      • seed default periods only when table is empty,
      • on every run, activate exactly the non-archived period covering date.today().
    • Enforces single-active behavior by deactivating all previously active periods before setting the period for today.
    • Emits explicit warning if no period covers current date (all remain inactive), improving operational diagnostics.
  • 🚀 Production startup alignment (docker-compose.prod.yml):
    • Server startup command now runs python /app/server/init_academic_periods.py after migrations and default settings bootstrap.
    • Removes manual post-deploy step to set an active academic period.
  • 🌐 Dashboard UX/text refinement (dashboard/src/dashboard.tsx):
    • Converted user-facing transliterated German strings to proper Umlauts in the dashboard (for example: "für", "prüfen", "Ferienüberschneidungen", "Vorfälle", "Ausfälle").

Notes for integrators:

  • On production boot, the active period is now derived from current date coverage automatically.
  • If customer calendars do not include today, startup logs a warning and dashboard banner will still guide admins to configure periods.

2026.1.0-alpha.15 (2026-03-31)

  • 🔌 TV Power Intent Phase 1 (server-side):
    • Scheduler now publishes retained QoS1 group-level intents to infoscreen/groups/{group_id}/power/intent.
    • Implemented deterministic intent computation helpers in scheduler/db_utils.py (compute_group_power_intent_basis, build_group_power_intent_body, compute_group_power_intent_fingerprint).
    • Implemented transition + heartbeat semantics in scheduler/scheduler.py: stable intent_id on heartbeat; new intent_id only on semantic transition.
    • Added startup publish and MQTT reconnect republish behavior for retained intent continuity.
    • Added poll-based expiry rule: expires_at = issued_at + max(3 × poll_interval_sec, 90s).
    • Added scheduler tests and canary validation artifacts: scheduler/test_power_intent_utils.py, scheduler/test_power_intent_scheduler.py, test_power_intent_canary.py, and TV_POWER_CANARY_VALIDATION_CHECKLIST.md.
  • 🗃️ Holiday data model scoping to academic periods:
    • Added period scoping for holidays via SchoolHoliday.academic_period_id (FK to academic periods) in models/models.py.
    • Added Alembic migration f3c4d5e6a7b8_scope_school_holidays_to_academic_.py to introduce FK/index/constraint updates for period-aware holiday storage.
    • Updated uniqueness semantics and indexing so holiday identity is evaluated in the selected academic period context.
  • 🔌 Holiday API hardening (server/routes/holidays.py):
    • Extended to period-scoped workflows for list/import/manual CRUD.
    • Added manual CRUD endpoints and behavior:
      • POST /api/holidays
      • PUT /api/holidays/<id>
      • DELETE /api/holidays/<id>
    • Enforced date-range validation against selected academic period for both import and manual writes.
    • Added duplicate prevention (normalized name/region matching with null-safe handling).
    • Implemented overlap policy:
      • Same normalized name+region overlaps (including adjacent ranges) are merged.
      • Different-identity overlaps are treated as conflicts (manual blocked, import skipped with details).
    • Import responses now include richer counters/details (inserted/updated/merged/skipped/conflicts).
  • 🔁 Recurrence integration updates:
    • Event holiday-skip exception regeneration now resolves holidays by academic_period_id instead of global holiday sets.
    • Updated event-side recurrence handling (server/routes/events.py) to keep EXDATE behavior in sync with period-scoped holidays.
  • 🖥️ Frontend integration (technical):
    • Updated holiday API client (dashboard/src/apiHolidays.ts) for period-aware list/upload and manual CRUD operations.
    • Settings holiday management (dashboard/src/settings.tsx) now binds import/list/manual CRUD to selected academic period and surfaces conflict/merge outcomes.
    • Dashboard and appointments holiday data loading updated to active-period context.
  • 📖 Documentation & release alignment:
    • Updated .github/copilot-instructions.md with period-scoped holiday conventions, overlap policy, and settings behavior.
    • Refactored root README.md to index-style documentation and archived historical implementation docs under docs/archive/.
    • Synchronized release line with user-facing version 2026.1.0-alpha.15 in dashboard/public/program-info.json.

Notes for integrators:

  • Holiday operations now require a clear academic period context; archived periods should be treated as read-only for holiday mutation flows.
  • Existing recurrence flows depend on period-scoped holiday sets; verify period assignment for recurring master events when validating skip-holidays behavior.

2026.1.0-alpha.14 (2026-01-28)

  • 🗓️ Ressourcen Page (Timeline View):
    • New frontend page: dashboard/src/ressourcen.tsx (357 lines) Parallel timeline view showing active events for all room groups
    • Uses Syncfusion ScheduleComponent with TimelineViews module for resource-based scheduling
    • Compact visualization: 65px row height per group, dynamically calculated total container height
    • Real-time event loading: Fetches events per group for current date range on mount and view/date changes
    • Timeline modes: Day (default) and Week views with date range calculation
    • Color-coded event bars: Uses getGroupColor() from groupColors.ts for group theme matching
    • Displays first active event per group with type, title, and time window
    • Filters out "Nicht zugeordnet" group from timeline display
    • Resource mapping: Each group becomes a timeline resource row, events mapped via ResourceId
    • Syncfusion modules: TimelineViews, Resize, DragAndDrop injected for rich interaction
  • 🎨 Ressourcen Styling:
    • New CSS file: dashboard/src/ressourcen.css (178 lines) with modern Material 3 design
    • Fixed CSS lint errors: Converted rgba() to modern rgb() notation with percentage alpha values (rgb(0 0 0 / 10%))
    • Removed unnecessary quotes from font-family names (Roboto, Oxygen, Ubuntu, Cantarell)
    • Fixed CSS selector specificity ordering (.e-schedule before .ressourcen-timeline-wrapper .e-schedule)
    • Card-based controls layout with shadow and rounded corners
    • Group ordering panel with scrollable list and action buttons
    • Responsive timeline wrapper with flex layout
  • 🔌 Group Order API:
    • New backend endpoints in server/routes/groups.py:
      • GET /api/groups/order Retrieve saved group display order (returns JSON with order array of group IDs)
      • POST /api/groups/order Persist group display order (accepts JSON with order array)
    • Order persistence: Stored in system_settings table with key group_display_order (JSON array of integers)
    • Automatic synchronization: Missing group IDs added to order, removed IDs filtered out
    • Frontend integration: Group order panel with drag up/down buttons, real-time reordering with backend sync
  • 🖥️ Frontend Technical:
    • State management: React hooks with unused setters removed (setTimelineView, setViewDate) to resolve lint warnings
    • TypeScript: Changed let to const for immutable end date calculation
    • UTC date parsing: Uses parseUTCDate callback to append 'Z' and ensure UTC interpretation
    • Event formatting: Capitalizes first letter of event type for display (e.g., "Website - Title")
    • Loading state: Shows loading indicator while fetching group/event data
    • Schedule height: Dynamic calculation based on groups.length * 65px + 100px for header
  • 📖 Documentation:
    • Updated .github/copilot-instructions.md:
      • Added Ressourcen page to "Recent changes" section (January 2026)
      • Added ressourcen.tsx and ressourcen.css to "Important files" list
      • Added Groups API order endpoints documentation
      • Added comprehensive Ressourcen page section to "Frontend patterns"
    • Updated README.md:
      • Added Ressourcen page to "Pages Overview" section with feature details
      • Added GET/POST /api/groups/order to Core Resources API section
    • Bumped version in dashboard/public/program-info.json to 2026.1.0-alpha.14 with user-facing changelog

Notes for integrators:

  • Group order API returns JSON with { "order": [1, 2, 3, ...] } structure (array of group IDs)
  • Timeline view automatically filters "Nicht zugeordnet" group for cleaner display
  • CSS follows modern Material 3 color-function notation (rgb(r g b / alpha%))
  • Syncfusion ScheduleComponent requires TimelineViews, Resize, and DragAndDrop modules injected

Backend technical work (post-release notes; no version bump):

  • 📊 Client Monitoring Infrastructure (Server-Side) (2026-03-10):
    • Database schema: New Alembic migration c1d2e3f4g5h6_add_client_monitoring.py (idempotent) adds:
      • client_logs table: Stores centralized logs with columns (id, client_uuid, timestamp, level, message, context, created_at)
      • Foreign key: client_logs.client_uuidclients.uuid (ON DELETE CASCADE)
      • Health monitoring columns added to clients table: current_event_id, current_process, process_status, process_pid, last_screenshot_analyzed, screen_health_status, last_screenshot_hash
      • Indexes for performance: (client_uuid, timestamp DESC), (level, timestamp DESC), (created_at DESC)
    • Data models (models/models.py):
      • New enums: LogLevel (ERROR, WARN, INFO, DEBUG), ProcessStatus (running, crashed, starting, stopped), ScreenHealthStatus (OK, BLACK, FROZEN, UNKNOWN)
      • New model: ClientLog with foreign key to Client (CASCADE on delete)
      • Extended Client model with 7 health monitoring fields
    • MQTT listener extensions (listener/listener.py):
      • New topic subscriptions: infoscreen/+/logs/error, infoscreen/+/logs/warn, infoscreen/+/logs/info, infoscreen/+/health
      • Log handler: Parses JSON payloads, creates ClientLog entries, validates client UUID exists (FK constraint)
      • Health handler: Updates client state from MQTT health messages
      • Enhanced heartbeat handler: Captures process_status, current_process, process_pid, current_event_id from payload
    • API endpoints (server/routes/client_logs.py):
      • GET /api/client-logs/<uuid>/logs Retrieve client logs with filters (level, limit, since); authenticated (admin_or_higher)
      • GET /api/client-logs/summary Get log counts by level per client for last 24h; authenticated (admin_or_higher)
      • GET /api/client-logs/monitoring-overview Aggregated monitoring overview for dashboard clients/statuses; authenticated (admin_or_higher)
      • GET /api/client-logs/recent-errors System-wide error monitoring; authenticated (admin_or_higher)
      • GET /api/client-logs/test Infrastructure validation endpoint (no auth required)
      • Blueprint registered in server/wsgi.py as client_logs_bp
    • Dev environment fix: Updated docker-compose.override.yml listener service to use working_dir: /workspace and direct command path for live code reload
  • 🖥️ Monitoring Dashboard Integration (2026-03-24):
    • Frontend monitoring dashboard (dashboard/src/monitoring.tsx) is active and wired to monitoring APIs
    • Superadmin-only route/menu integration completed in dashboard/src/App.tsx
    • Added dashboard monitoring API client (dashboard/src/apiClientMonitoring.ts) for overview and recent errors
  • 🐛 Presentation Flags Persistence Fix (2026-03-24):
    • Fixed persistence for presentation flags page_progress and auto_progress across create/update and detached-occurrence flows
    • API serialization now reliably returns stored values for presentation behavior fields
  • 📡 MQTT Protocol Extensions:
    • New log topics: infoscreen/{uuid}/logs/{error|warn|info} with JSON payload (timestamp, message, context)
    • New health topic: infoscreen/{uuid}/health with metrics (expected_state, actual_state, health_metrics)
    • Enhanced heartbeat: infoscreen/{uuid}/heartbeat now includes current_process, process_pid, process_status, current_event_id
    • QoS levels: ERROR/WARN logs use QoS 1 (at least once), INFO/health use QoS 0 (fire and forget)
  • 📖 Documentation:
    • New file: CLIENT_MONITORING_SPECIFICATION.md Comprehensive 20-section technical spec for client-side implementation (MQTT protocol, process monitoring, auto-recovery, payload formats, testing guide)
    • New file: CLIENT_MONITORING_IMPLEMENTATION_GUIDE.md 5-phase implementation guide (database, backend, client watchdog, dashboard UI, testing)
    • Updated .github/copilot-instructions.md: Added MQTT topics section, client monitoring integration notes
  • Validation:
    • End-to-end testing completed: MQTT message → listener → database → API confirmed working
    • Test flow: Published message to infoscreen/{real-uuid}/logs/error → listener logs showed receipt → database stored entry → test API returned log data
    • Known client UUIDs validated: 9b8d1856-ff34-4864-a726-12de072d0f77, 7f65c615-5827-4ada-9ac8-4727c2e8ee55, bdbfff95-0b2b-4265-8cc7-b0284509540a

Notes for integrators:

  • Tiered logging strategy: ERROR/WARN always centralized (QoS 1), INFO dev-only (QoS 0), DEBUG local-only
  • Monitoring dashboard is implemented and consumes /api/client-logs/monitoring-overview, /api/client-logs/recent-errors, and /api/client-logs/<uuid>/logs
  • Foreign key constraint prevents logging for non-existent clients (data integrity enforced)
  • Migration is idempotent and can be safely rerun after interruption
  • Use GET /api/client-logs/test for quick infrastructure validation without authentication

2025.1.0-beta.1 (TBD)

  • 🔐 User Management & Role-Based Access Control:
    • Backend: Implemented comprehensive user management API (server/routes/users.py) with 6 endpoints (GET, POST, PUT, DELETE users + password reset).
    • Data model: Extended User with 7 audit/security fields via Alembic migration (4f0b8a3e5c20_add_user_audit_fields.py):
      • last_login_at, last_password_change_at: TIMESTAMP (UTC) for auth event tracking
      • failed_login_attempts, last_failed_login_at: Security monitoring for brute-force detection
      • locked_until: TIMESTAMP placeholder for account lockout (infrastructure in place, not yet enforced)
      • deactivated_at, deactivated_by: Soft-delete audit trail (FK self-reference)
    • Role hierarchy: 4-tier privilege escalation (user → editor → admin → superadmin) enforced at API and UI levels:
      • Admin cannot see, create, or manage superadmin accounts
      • Admin can manage user/editor/admin roles only
      • Superadmin can manage all roles including other superadmins
    • Auth routes enhanced (server/routes/auth.py):
      • Login: Sets last_login_at, resets failed_login_attempts on success; increments failed_login_attempts and last_failed_login_at on failure
      • Password change: Sets last_password_change_at on both self-service and admin reset
      • New endpoint: PUT /api/auth/change-password for self-service password change (all authenticated users; requires current password verification)
    • User API security:
      • Admin cannot reset superadmin passwords
      • Self-account protections: cannot change own role/status, cannot delete self
      • Admin cannot use password reset endpoint for their own account (backend check enforces self-service requirement)
      • All user responses include audit fields in camelCase (lastLoginAt, lastPasswordChangeAt, failedLoginAttempts, deactivatedAt, deactivatedBy)
    • Soft-delete pattern: Deactivation by default (sets deactivated_at and deactivated_by); hard-delete superadmin-only
  • 🖥️ Frontend User Management:
    • New page: dashboard/src/users.tsx Full CRUD interface (820 lines) with Syncfusion components
    • GridComponent: 20 per page (configurable), sortable columns (ID, username, role), custom action button template with role-based visibility
    • Statistics cards: Total users, active (non-deactivated), inactive (deactivated) counts
    • Dialogs: Create (username/password/role/status), Edit (with self-edit protections), Password Reset (admin only, no current password required), Delete (superadmin only, self-check), Details (read-only audit info with formatted timestamps)
    • Role badges: Color-coded display (user: gray, editor: blue, admin: green, superadmin: red)
    • Audit information display: last login, password change, last failed login, deactivation timestamps and deactivating user
    • Self-protection: Delete button hidden for current user (prevents accidental self-deletion)
    • Menu visibility: "Benutzer" sidebar item only visible to admin+ (role-gated in App.tsx)
  • 💬 Header User Menu:
    • Enhanced top-right dropdown with "Passwort ändern" (lock icon), "Profil", and "Abmelden"
    • Self-service password change dialog: Available to all authenticated users; requires current password verification, new password min 6 chars, must match confirm field
    • Implemented with Syncfusion DropDownButton (@syncfusion/ej2-react-splitbuttons)
  • 🔌 API Client:
    • New file: dashboard/src/apiUsers.ts Type-safe TypeScript client (143 lines) for user operations
    • Functions: listUsers(), getUser(), createUser(), updateUser(), resetUserPassword(), deleteUser()
    • All functions include proper error handling and camelCase JSON mapping
  • 📖 Documentation:
    • Updated .github/copilot-instructions.md: Added comprehensive sections on user model audit fields, user management API routes, auth routes, header menu, and user management page implementation
    • Updated README.md: Added user management to Key Features, API endpoints (User Management + Authentication sections), Pages Overview, and Security & Authentication sections with RBAC details
    • Updated TECH-CHANGELOG.md: Documented all technical changes and integration notes

Notes for integrators:

  • User CRUD endpoints accept/return all audit fields in camelCase
  • Admin password reset (PUT /api/users/<id>/password) cannot be used for admin's own account; users must use self-service endpoint
  • Frontend enforces role-gated menu visibility; backend validates all role transitions to prevent privilege escalation
  • Soft-delete is default; hard-delete (superadmin-only) requires explicit confirmation
  • Audit fields populated automatically on login/logout/password-change/deactivation events

Backend rework (post-release notes; no version bump):

  • 🧩 Dev Container hygiene: Remote Containers runs on UI (remote.extensionKind), removed in-container install to prevent reappearance loops; switched postCreateCommand to npm ci for reproducible dashboard installs; postStartCommand aliases made idempotent.
  • 🔄 Serialization: Consolidated snake_case→camelCase via server/serializers.py for all JSON outputs; ensured enums/UTC datetimes serialize consistently across routes.
  • 🕒 Time handling: Normalized naive timestamps to UTC in all back-end comparisons (events, scheduler, groups) and kept ISO strings without Z in API responses; frontend appends Z.
  • 📡 Streaming: Stabilized range-capable endpoint (/api/eventmedia/stream/<media_id>/<filename>), clarified client handling; scheduler emits basic HEAD-probe metadata (mime_type, size, accept_ranges).
  • 📅 Recurrence/exceptions: Ensured EXDATE tokens (RFC 5545 UTC) align with occurrence start; detached-occurrence flow confirmed via POST /api/events/<id>/occurrences/<date>/detach.
  • 🧰 Routes cleanup: Applied dict_to_camel_case() before jsonify() uniformly; verified Session lifecycle consistency (open/commit/close) across blueprints.
  • 🔄 API Naming Convention Standardization:
    • Created server/serializers.py with dict_to_camel_case() and dict_to_snake_case() utilities for consistent JSON serialization
    • Events API refactored: GET /api/events and GET /api/events/<id> now return camelCase JSON (id, subject, startTime, endTime, type, groupId, etc.) instead of PascalCase
    • Internal event dictionaries use snake_case keys, then converted to camelCase via dict_to_camel_case() before jsonify()
    • Breaking: External API consumers must update field names from PascalCase to camelCase
  • UTC Time Handling:
    • Standardized datetime handling: Database stores timestamps in UTC (naive timestamps normalized by backend)
    • API returns ISO strings without 'Z' suffix: "2025-11-27T20:03:00"
    • Frontend appends 'Z' to parse as UTC and displays in user's local timezone via toLocaleTimeString('de-DE')
    • All time comparisons use UTC; date.toISOString() sends UTC back to API
  • 🖥️ Dashboard Major Redesign:
    • Completely redesigned dashboard with card-based layout for Raumgruppen (room groups)
    • Global statistics summary card: total infoscreens, online/offline counts, warning groups
    • Filter buttons with dynamic counts: All, Online, Offline, Warnings
    • Active event display per group: shows currently playing content with type icon, title, date ("Heute"/"Morgen"/date), and time range
    • Health visualization: color-coded progress bars showing online/offline ratio per group
    • Expandable client details: shows last alive timestamps with human-readable format ("vor X Min.", "vor X Std.", "vor X Tagen")
    • Bulk restart functionality: restart all offline clients in a group
    • Manual refresh button with toast notifications
    • 15-second auto-refresh interval
    • "Nicht zugeordnet" group always appears last in sorted list
  • 🎨 Frontend Technical:
    • Dashboard (dashboard/src/dashboard.tsx): Uses Syncfusion ButtonComponent, ToastComponent, and card CSS classes
    • Appointments page updated to map camelCase API responses to internal PascalCase for Syncfusion compatibility
    • Time formatting functions (formatEventTime, formatEventDate) handle UTC string parsing with 'Z' appending
    • TypeScript lint errors resolved: unused error variables removed, null safety checks added with optional chaining
  • 📖 Documentation:
    • Updated .github/copilot-instructions.md with comprehensive sections on:
      • API patterns: JSON serialization, datetime handling conventions
      • Frontend patterns: API response format, UTC time parsing
      • Dashboard page overview with features
      • Conventions & gotchas: datetime and JSON naming guidelines
    • Updated README.md with recent changes, API response format section, and dashboard page details

Notes for integrators:

  • Breaking change: All Events API endpoints now return camelCase field names. Update client code accordingly.
  • Frontend must append 'Z' to API datetime strings before parsing: const utcStr = dateStr.endsWith('Z') ? dateStr : dateStr + 'Z'; new Date(utcStr);
  • Use dict_to_camel_case() from server/serializers.py for any new API endpoints returning JSON
  • Dev container: prefer npm ci and UI-only Remote Containers to avoid extension drift in-container.

Component build metadata template (for traceability)

Record component builds under the unified app version when releasing:

Component builds for this release
- API: image tag `ghcr.io/robbstarkaustria/api:<short-sha>` (commit `<sha>`)
- Dashboard: image tag `ghcr.io/robbstarkaustria/dashboard:<short-sha>` (commit `<sha>`)
- Scheduler: image tag `ghcr.io/robbstarkaustria/scheduler:<short-sha>` (commit `<sha>`)
- Listener: image tag `ghcr.io/robbstarkaustria/listener:<short-sha>` (commit `<sha>`)
- Worker: image tag `ghcr.io/robbstarkaustria/worker:<short-sha>` (commit `<sha>`)

This is informational (build metadata) and does not change the user-facing version number.

2025.1.0-alpha.11 (2025-11-05)

  • 🗃️ Data model & API:
    • Added muted (Boolean) to Event with Alembic migration; create/update and GET endpoints now accept, persist, and return muted alongside autoplay, loop, and volume for video events.
    • Video event fields consolidated: event_media_id, autoplay, loop, volume, muted.
  • 🔗 Streaming:
    • Added range-capable streaming endpoint: GET /api/eventmedia/stream/<media_id>/<filename> (supports byte-range requests 206 for seeking).
    • Scheduler: Performs a best-effort HEAD probe for video stream URLs and includes basic metadata in the emitted payload (mime_type, size, accept_ranges). Placeholders added for duration, resolution, bitrate, qualities, thumbnails, checksum.
  • 🖥️ Frontend/Dashboard:
    • Settings page refactored to nested tabs with controlled tab selection (selectedItem) to prevent sub-tab jumps.
    • Settings → Events → Videos: Added system-wide defaults with load/save via system settings keys: video_autoplay, video_loop, video_volume, video_muted.
    • Event modal (CustomEventModal): Exposes per-event video options including “Ton aus” (muted) and initializes all video fields from system defaults when creating new events.
    • Academic Calendar (Settings): Merged “Schulferien Import” and “Liste” into a single sub-tab “📥 Import & Liste”.
  • 📖 Documentation:
    • Updated README.md and .github/copilot-instructions.md for video payload (incl. muted), streaming endpoint (206), nested Settings tabs, and video defaults keys; clarified client handling of video payloads.
    • Updated dashboard/public/program-info.json (user-facing changelog) and bumped version to 2025.1.0-alpha.11 with corresponding UI/UX notes.

Notes for integrators:

  • Clients should parse event_type and handle the nested video payload, honoring autoplay, loop, volume, and muted. Use the streaming endpoint with HTTP Range for seeking.
  • System settings keys for video defaults: video_autoplay, video_loop, video_volume, video_muted.

2025.1.0-alpha.10 (2025-10-25)

  • No new developer-facing changes in this release.
  • UI/UX updates are documented in dashboard/public/program-info.json:
    • Event modal: Surfaced video options (Autoplay, Loop, Volume).
    • FileManager: Increased upload limits (Full-HD); client-side duration validation (max 10 minutes).

2025.1.0-alpha.9 (2025-10-19)

  • 🗓️ Events/API:
    • Implemented new webuntis event type. Event creation now resolves the URL from the system setting supplement_table_url; returns 400 if unset.
    • Removed obsolete webuntis-url settings endpoints. Use GET/POST /api/system-settings/supplement-table for URL and enabled state (shared for WebUntis/Vertretungsplan).
    • Initialization defaults: dropped webuntis_url; updated supplement_table_url description to “Vertretungsplan / WebUntis”.
  • 🚦 Scheduler payloads:
    • Unified Website/WebUntis payload: both emit a nested website object { "type": "browser", "url": "…" }; event_type remains either website or webuntis for dispatch.
    • Payloads now include a top-level event_type string for all events to aid client dispatch.
  • 🖥️ Frontend/Dashboard:
    • Program info updated to 2025.1.0-alpha.13 with release notes.
    • Settings → Events: WebUntis now uses the existing Supplement-Table URL; no separate WebUntis URL field.
    • Event modal: WebUntis type behaves like Website (no per-event URL input).
  • 📖 Documentation:
    • Added MQTT_EVENT_PAYLOAD_GUIDE.md (message structure, client best practices, versioning).
    • Added WEBUNTIS_EVENT_IMPLEMENTATION.md (design notes, admin setup, testing checklist).
    • Updated .github/copilot-instructions.md and README.md for the unified Website/WebUntis handling and settings usage.

Notes for integrators:

  • If you previously integrated against /api/system-settings/webuntis-url, migrate to /api/system-settings/supplement-table.
  • Clients should now parse event_type and use the corresponding nested payload (presentation, website, …). webuntis and website should be handled identically (nested website payload).

2025.1.0-alpha.8 (2025-10-18)

  • 🛠️ Backend: Seeded presentation defaults (presentation_interval, presentation_page_progress, presentation_auto_progress) in system settings; applied on event creation.
  • 🗃️ Data model: Added page_progress and auto_progress fields to Event (with Alembic migration).
  • 🗓️ Scheduler: Now publishes only currently active events per group (at "now"); clears retained topics by publishing [] for groups with no active events; normalizes naive timestamps and compares times in UTC; presentation payloads include page_progress and auto_progress.
  • 🖥️ Dashboard: Settings → Events tab now includes Presentations defaults (interval, page-progress, auto-progress) with load/save via API; event modal applies defaults on create and persists per-event values on edit.
  • 📖 Docs: Updated README and Copilot instructions for new scheduler behavior, UTC handling, presentation defaults, and per-event flags.

2025.1.0-alpha.11 (2025-10-16)

  • Settings page: New tab layout (Syncfusion) with role-based visibility Tabs: 📅 Academic Calendar, 🖥️ Display & Clients, 🎬 Media & Files, 🗓️ Events, ⚙️ System.
  • 🛠️ Settings (Technical): API calls now use relative /api paths via the Vite proxy (prevents CORS and double /api).
  • 📖 Docs: README updated for settings page (tabs) and system settings API.

2025.1.0-alpha.10 (2025-10-15)

  • 🔐 Auth: Login and user management implemented (role-based, persistent sessions).
  • 🧩 Frontend: Syncfusion SplitButtons integrated (react-splitbuttons) and Vite config updated for pre-bundling.
  • 🐛 Fix: Import error @syncfusion/ej2-react-splitbuttons instructions added to README (optimizeDeps + volume reset).

2025.1.0-alpha.9 (2025-10-14)

  • UI: Unified deletion workflow for appointments all types (single, single instance, entire series) handled with custom dialogs.
  • 🔧 Frontend: Syncfusion RecurrenceAlert and DeleteAlert intercepted and replaced with custom dialogs (including final confirmation for series deletion).
  • 📖 Docs: README and Copilot instructions expanded for deletion workflow and dialog handling.

2025.1.0-alpha.8 (2025-10-11)

  • 🎨 Theme: Migrated to Syncfusion Material 3; centralized CSS imports in main.tsx
  • 🧹 Cleanup: Tailwind CSS completely removed (packages, PostCSS, Stylelint, config files)
  • 🧩 Group management: "infoscreen_groups" migrated to Syncfusion components (Buttons, Dialogs, DropDownList, TextBox); improved spacing
  • 🔔 Notifications: Unified toast/dialog wording; last alert usage replaced
  • 📖 Docs: README and Copilot instructions updated (Material 3, centralized styles, no Tailwind)

2025.1.0-alpha.7 (2025-09-21)

  • 🧭 UI: Period selection (Syncfusion) next to group selection; compact layout
  • Display: Badge for existing holiday plan + counter Holidays in view
  • 🛠️ API: Endpoints for academic periods (list, active GET/POST, for_date)
  • 📅 Scheduler: By default, no scheduling during holidays; block display like all-day event; black text color
  • 📤 Holidays: Upload from TXT/CSV (headless TXT uses columns 24)
  • 🔧 UX: Switches in a row; dropdown widths optimized

2025.1.0-alpha.6 (2025-09-20)

  • 🗓️ NEW: Academic periods system support for school years, semesters, trimesters
  • 🏗️ DATABASE: New 'academic_periods' table for time-based organization
  • 🔗 EXTENDED: Events and media can now optionally be linked to an academic period
  • 📊 ARCHITECTURE: Fully backward-compatible implementation for gradual rollout
  • ⚙️ TOOLS: Automatic creation of standard school years for Austrian schools

2025.1.0-alpha.5 (2025-09-14)

  • Backend: Complete redesign of backend handling for group assignments of new clients and steps for changing group assignment.

2025.1.0-alpha.4 (2025-09-01)

  • Deployment: Base structure for deployment tested and optimized.
  • FIX: Program error when switching view on media page fixed.

2025.1.0-alpha.3 (2025-08-30)

  • NEW: Program info page with dynamic data, build info, and changelog.
  • NEW: Logout functionality implemented.
  • FIX: Sidebar width corrected in collapsed state.

2025.1.0-alpha.2 (2025-08-29)

  • INFO: Analysis and display of used open-source libraries.

2025.1.0-alpha.1 (2025-08-28)

  • Initial project setup and base structure.