- 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
114 lines
6.2 KiB
Markdown
114 lines
6.2 KiB
Markdown
# Copilot instructions for infoscreen_2025
|
|
|
|
## Purpose
|
|
This file is a concise, high-signal brief for coding agents.
|
|
It is not a changelog and not a full architecture handbook.
|
|
|
|
## TL;DR
|
|
- Stack: Flask API + MariaDB, React/Vite dashboard, MQTT listener, scheduler, worker.
|
|
- Main areas:
|
|
- API logic in `server/`
|
|
- Scheduler logic in `scheduler/`
|
|
- UI logic in `dashboard/src/`
|
|
- Keep changes minimal, match existing patterns, and update docs in the same commit when behavior changes.
|
|
|
|
## Fast file map
|
|
- `scheduler/scheduler.py` - scheduler loop, MQTT event publishing, TV power intent publishing, crash auto-recovery, command expiry sweep
|
|
- `scheduler/db_utils.py` - event formatting, power-intent helpers, crash recovery helpers, command expiry sweep
|
|
- `listener/listener.py` - discovery/heartbeat/log/screenshot/service_failed MQTT consumption
|
|
- `server/init_academic_periods.py` - idempotent academic-period seeding + auto-activation for current date
|
|
- `server/initialize_database.py` - migration + bootstrap orchestration for local/manual setup
|
|
- `server/routes/events.py` - event CRUD, recurrence handling, UTC normalization
|
|
- `server/routes/eventmedia.py` - file manager, media upload/stream endpoints
|
|
- `server/routes/groups.py` - group lifecycle, alive status, order persistence
|
|
- `server/routes/system_settings.py` - system settings CRUD and supplement-table endpoint
|
|
- `server/routes/clients.py` - client metadata, restart/shutdown/restart_app command issuing, command status, crashed/service_failed alert endpoints
|
|
- `dashboard/src/settings.tsx` - settings UX and system-defaults integration
|
|
- `dashboard/src/components/CustomEventModal.tsx` - event creation/editing UX
|
|
- `dashboard/src/monitoring.tsx` - superadmin monitoring page
|
|
- `TV_POWER_INTENT_SERVER_CONTRACT_V1.md` - Phase 1 TV power contract
|
|
|
|
## Service picture
|
|
- API: `server/` on `:8000` (health: `/health`)
|
|
- Dashboard: `dashboard/` (dev `:5173`, proxied API calls)
|
|
- MQTT broker: Mosquitto (`mosquitto/config/mosquitto.conf`)
|
|
- Listener: MQTT consumer that updates server-side state
|
|
- Scheduler: publishes active events and group-level TV power intents
|
|
- Nginx: routes `/api/*` and `/screenshots/*` to API, dashboard otherwise
|
|
- Prod bootstrap: `docker-compose.prod.yml` server command runs migrations, defaults init, and academic-period init before Gunicorn start
|
|
|
|
## Non-negotiable conventions
|
|
- Datetime:
|
|
- Store/compare in UTC.
|
|
- API returns ISO strings without `Z` in many routes.
|
|
- Frontend must append `Z` before parsing if needed.
|
|
- JSON naming:
|
|
- Backend internals use snake_case.
|
|
- API responses use camelCase (via `server/serializers.py`).
|
|
- DB host in containers: `db` (not localhost).
|
|
- Never put secrets in docs.
|
|
|
|
## MQTT contracts
|
|
- Event list topic (retained): `infoscreen/events/{group_id}`
|
|
- Group assignment topic (retained): `infoscreen/{uuid}/group_id`
|
|
- Heartbeat topic: `infoscreen/{uuid}/heartbeat`
|
|
- Logs topic family: `infoscreen/{uuid}/logs/{error|warn|info}`
|
|
- Health topic: `infoscreen/{uuid}/health`
|
|
- Dashboard screenshot topic: `infoscreen/{uuid}/dashboard`
|
|
- Client command topic (QoS1, non-retained): `infoscreen/{uuid}/commands` (compat alias: `infoscreen/{uuid}/command`)
|
|
- Client command ack topic (QoS1, non-retained): `infoscreen/{uuid}/commands/ack` (compat alias: `infoscreen/{uuid}/command/ack`)
|
|
- Service-failed topic (retained, client→server): `infoscreen/{uuid}/service_failed`
|
|
- TV power intent Phase 1 topic (retained, QoS1): `infoscreen/groups/{group_id}/power/intent`
|
|
|
|
TV power intent Phase 1 rules:
|
|
- Schema version is `"1.0"`.
|
|
- Group-only scope in Phase 1.
|
|
- Heartbeat publish keeps `intent_id`; semantic transition rotates `intent_id`.
|
|
- Expiry rule: `expires_at = issued_at + max(3 x poll_interval_sec, 90s)`.
|
|
- Canonical contract is `TV_POWER_INTENT_SERVER_CONTRACT_V1.md`.
|
|
|
|
## Backend patterns
|
|
- Routes in `server/routes/*`, registered in `server/wsgi.py`.
|
|
- Use one request-scoped DB session, commit on mutation, always close session.
|
|
- Keep enum/datetime serialization JSON-safe.
|
|
- Maintain UTC-safe comparisons in scheduler and routes.
|
|
- Keep recurrence handling backend-driven and consistent with exceptions.
|
|
- Academic periods bootstrap is idempotent and should auto-activate period covering `date.today()` when available.
|
|
|
|
## Frontend patterns
|
|
- Use Syncfusion-based patterns already present in dashboard.
|
|
- Keep API requests relative (`/api/...`) to use Vite proxy in dev.
|
|
- Respect `FRONTEND_DESIGN_RULES.md` for component and styling conventions.
|
|
- Keep role-gated UI behavior aligned with backend authorization.
|
|
- Holiday status banner in dashboard should render from computed state and avoid stale message reuse in 3rd-party UI components.
|
|
|
|
## Environment variables (high-value)
|
|
- Scheduler: `POLL_INTERVAL_SECONDS`, `REFRESH_SECONDS`
|
|
- Power intent: `POWER_INTENT_PUBLISH_ENABLED`, `POWER_INTENT_HEARTBEAT_ENABLED`, `POWER_INTENT_EXPIRY_MULTIPLIER`, `POWER_INTENT_MIN_EXPIRY_SECONDS`
|
|
- Monitoring: `PRIORITY_SCREENSHOT_TTL_SECONDS`
|
|
- Crash recovery: `CRASH_RECOVERY_ENABLED`, `CRASH_RECOVERY_GRACE_SECONDS`, `CRASH_RECOVERY_LOCKOUT_MINUTES`, `CRASH_RECOVERY_COMMAND_EXPIRY_SECONDS`
|
|
- Core: `DB_CONN`, `DB_USER`, `DB_PASSWORD`, `DB_HOST`, `DB_NAME`, `ENV`
|
|
- MQTT auth/connectivity: `MQTT_BROKER_HOST`, `MQTT_BROKER_PORT`, `MQTT_USER`, `MQTT_PASSWORD` (listener/scheduler/server should use authenticated broker access)
|
|
|
|
## Edit guardrails
|
|
- Do not edit generated assets in `dashboard/dist/`.
|
|
- Do not change CI/build outputs unless explicitly intended.
|
|
- Preserve existing API behavior unless task explicitly requires a change.
|
|
- Prefer links to canonical docs instead of embedding long historical notes here.
|
|
|
|
## Documentation sync rule
|
|
When services/MQTT/API/UTC/env behavior changes:
|
|
1. Update this file (concise deltas only).
|
|
2. Update canonical docs where details live.
|
|
3. Update changelogs separately (`TECH-CHANGELOG.md`, `DEV-CHANGELOG.md`, `dashboard/public/program-info.json` as appropriate).
|
|
|
|
## Canonical docs map
|
|
- Repo entry: `README.md`
|
|
- Instruction governance: `AI-INSTRUCTIONS-MAINTENANCE.md`
|
|
- Technical release details: `TECH-CHANGELOG.md`
|
|
- Workspace/development notes: `DEV-CHANGELOG.md`
|
|
- MQTT payload details: `MQTT_EVENT_PAYLOAD_GUIDE.md`
|
|
- TV power contract: `TV_POWER_INTENT_SERVER_CONTRACT_V1.md`
|
|
- Frontend patterns: `FRONTEND_DESIGN_RULES.md`
|
|
- Archived historical docs: `docs/archive/`
|